diff --git a/imphnen-backend/src/bin/seed_teams.rs b/imphnen-backend/src/bin/seed_teams.rs index 3cd0611..f12cf2c 100644 --- a/imphnen-backend/src/bin/seed_teams.rs +++ b/imphnen-backend/src/bin/seed_teams.rs @@ -1,4 +1,4 @@ -use imphnen_iam::TeamsSchema; +use imphnen_iam::v1::teams::TeamsSchema; use imphnen_utils::get_iso_date; use std::error::Error; use surrealdb::{opt::auth::Root, sql::Thing}; diff --git a/imphnen-cms/src/lib.rs b/imphnen-cms/src/lib.rs index 3789969..2dab4c8 100644 --- a/imphnen-cms/src/lib.rs +++ b/imphnen-cms/src/lib.rs @@ -1,2 +1,9 @@ pub mod v1; -pub use v1::*; + +pub use v1::landing; +pub use v1::landing::events; +pub use v1::landing::testimonials; +pub use v1::landing::events::events_public_routes; +pub use v1::landing::events::events_protected_routes; +pub use v1::landing::testimonials::testimonials_public_routes; +pub use v1::landing::testimonials::testimonials_protected_routes; diff --git a/imphnen-cms/src/v1/landing/events/mod.rs b/imphnen-cms/src/v1/landing/events/mod.rs index c6ab5d1..8b7012d 100644 --- a/imphnen-cms/src/v1/landing/events/mod.rs +++ b/imphnen-cms/src/v1/landing/events/mod.rs @@ -9,36 +9,30 @@ pub mod events_repository; pub mod events_schema; pub mod events_service; -pub use events_controller::*; -pub use events_dto::*; -pub use events_repository::*; -pub use events_schema::*; -pub use events_service::*; +// Export only the necessary public items +pub use events_dto::{ + EventsCreateRequestDto, + EventsUpdateRequestDto, + EventsListItemDto, + EventsDetailItemDto, +}; +pub use events_controller::{ + get_event_list, + get_event_by_id, + post_create_event, + patch_update_event, + delete_event, +}; pub fn events_public_routes() -> Router { Router::new() - .route( - "/cms/landing/events", - get(events_controller::get_event_list), - ) - .route( - "/cms/landing/events/detail/{id}", - get(events_controller::get_event_by_id), - ) + .route("/cms/landing/events", get(get_event_list)) + .route("/cms/landing/events/detail/{id}", get(get_event_by_id)) } pub fn events_protected_routes() -> Router { Router::new() - .route( - "/cms/landing/events/create", - post(events_controller::post_create_event), - ) - .route( - "/cms/landing/events/update/{id}", - patch(events_controller::patch_update_event), - ) - .route( - "/cms/landing/events/delete/{id}", - delete(events_controller::delete_event), - ) + .route("/cms/landing/events/create", post(post_create_event)) + .route("/cms/landing/events/update/{id}", patch(patch_update_event)) + .route("/cms/landing/events/delete/{id}", delete(delete_event)) } diff --git a/imphnen-cms/src/v1/landing/mod.rs b/imphnen-cms/src/v1/landing/mod.rs index 1c0a16a..c24ed04 100644 --- a/imphnen-cms/src/v1/landing/mod.rs +++ b/imphnen-cms/src/v1/landing/mod.rs @@ -1,5 +1,7 @@ pub mod events; pub mod testimonials; -pub use events::*; -pub use testimonials::*; +pub use events::events_public_routes; +pub use events::events_protected_routes; +pub use testimonials::testimonials_public_routes; +pub use testimonials::testimonials_protected_routes; diff --git a/imphnen-cms/src/v1/landing/testimonials/mod.rs b/imphnen-cms/src/v1/landing/testimonials/mod.rs index 3ee60e2..c0c9afc 100644 --- a/imphnen-cms/src/v1/landing/testimonials/mod.rs +++ b/imphnen-cms/src/v1/landing/testimonials/mod.rs @@ -9,36 +9,30 @@ pub mod testimonials_repository; pub mod testimonials_schema; pub mod testimonials_service; -pub use testimonials_controller::*; -pub use testimonials_dto::*; -pub use testimonials_repository::*; -pub use testimonials_schema::*; -pub use testimonials_service::*; +// Export only the necessary public items +pub use testimonials_dto::{ + TestimonialsCreateRequestDto, + TestimonialsUpdateRequestDto, + TestimonialsListItemDto, + TestimonialsDetailItemDto, +}; +pub use testimonials_controller::{ + get_testimonial_list, + get_testimonial_by_id, + post_create_testimonial, + patch_update_testimonial, + delete_testimonial, +}; pub fn testimonials_public_routes() -> Router { Router::new() - .route( - "/cms/landing/testimonials", - get(testimonials_controller::get_testimonial_list), - ) - .route( - "/cms/landing/testimonials/detail/{id}", - get(testimonials_controller::get_testimonial_by_id), - ) + .route("/cms/landing/testimonials", get(get_testimonial_list)) + .route("/cms/landing/testimonials/detail/{id}", get(get_testimonial_by_id)) } pub fn testimonials_protected_routes() -> Router { Router::new() - .route( - "/cms/landing/testimonials/create", - post(testimonials_controller::post_create_testimonial), - ) - .route( - "/cms/landing/testimonials/update/{id}", - patch(testimonials_controller::patch_update_testimonial), - ) - .route( - "/cms/landing/testimonials/delete/{id}", - delete(testimonials_controller::delete_testimonial), - ) + .route("/cms/landing/testimonials/create", post(post_create_testimonial)) + .route("/cms/landing/testimonials/update/{id}", patch(patch_update_testimonial)) + .route("/cms/landing/testimonials/delete/{id}", delete(delete_testimonial)) } diff --git a/imphnen-cms/src/v1/mod.rs b/imphnen-cms/src/v1/mod.rs index 078f4f7..eb87bb2 100644 --- a/imphnen-cms/src/v1/mod.rs +++ b/imphnen-cms/src/v1/mod.rs @@ -1,3 +1,8 @@ pub mod landing; -pub use landing::*; +pub use landing::events; +pub use landing::testimonials; +pub use landing::events::events_public_routes; +pub use landing::events::events_protected_routes; +pub use landing::testimonials::testimonials_public_routes; +pub use landing::testimonials::testimonials_protected_routes; diff --git a/imphnen-dimentorin/src/lib.rs b/imphnen-dimentorin/src/lib.rs index 3789969..03b2d4b 100644 --- a/imphnen-dimentorin/src/lib.rs +++ b/imphnen-dimentorin/src/lib.rs @@ -1,2 +1,5 @@ pub mod v1; -pub use v1::*; + +// Explicitly export only what's needed from v1 +pub use v1::dimentorin_router; +pub use v1::mentors::mentors_router; diff --git a/imphnen-dimentorin/src/v1/mentors/mentors_dto.rs b/imphnen-dimentorin/src/v1/mentors/mentors_dto.rs index 549edc2..3467739 100644 --- a/imphnen-dimentorin/src/v1/mentors/mentors_dto.rs +++ b/imphnen-dimentorin/src/v1/mentors/mentors_dto.rs @@ -167,7 +167,7 @@ pub struct MentorUserRegisterRequestDto { message = "Password must have at least 8 characters" ))] #[validate(custom( - function = "imphnen_iam::auth_dto::validate_password_complexity", + function = "imphnen_iam::v1::auth::auth_dto::validate_password_complexity", message = "Password must include uppercase, lowercase, number, and special character" ))] pub password: String, diff --git a/imphnen-dimentorin/src/v1/mentors/mentors_repository.rs b/imphnen-dimentorin/src/v1/mentors/mentors_repository.rs index 54d103f..bfeb525 100644 --- a/imphnen-dimentorin/src/v1/mentors/mentors_repository.rs +++ b/imphnen-dimentorin/src/v1/mentors/mentors_repository.rs @@ -2,7 +2,8 @@ use anyhow::{Result, bail}; use imphnen_iam::{get_id, make_thing}; use surrealdb::sql::Thing; -use crate::v1::mentors::{MentorDetailWithUserDto, MentorInsertDto, MentorSchema}; +use crate::v1::mentors::mentors_dto::MentorDetailWithUserDto; +use crate::v1::mentors::{MentorInsertDto, MentorSchema}; use imphnen_libs::{AppState, MetaRequestDto, ResourceEnum, ResponseListSuccessDto}; use imphnen_utils::{DetailQueryBuilder, QueryListBuilder, get_iso_date}; use serde_json::{Map, Value}; diff --git a/imphnen-dimentorin/src/v1/mentors/mod.rs b/imphnen-dimentorin/src/v1/mentors/mod.rs index 63f2c94..09dbe42 100644 --- a/imphnen-dimentorin/src/v1/mentors/mod.rs +++ b/imphnen-dimentorin/src/v1/mentors/mod.rs @@ -9,11 +9,42 @@ pub mod mentors_repository; pub mod mentors_schema; pub mod mentors_service; -pub use mentors_controller::*; -pub use mentors_dto::*; -pub use mentors_repository::*; -pub use mentors_schema::*; -pub use mentors_service::*; +// Explicitly export only public controller functions and key types +pub use mentors_controller::{ + post_register_mentor, + get_mentor_list, + get_mentor_by_id, + put_update_mentor, + delete_mentor, + put_verify_mentor, + get_mentor_me, + put_update_mentor_me, + put_update_mentor_no_id, + get_mentor_status, +}; + +// Export key DTO types used across the API +pub use mentors_dto::{ + MentorListResponseDto, + MentorDetailResponseDto, + MentorRegisterResponseDto, + MentorUpdateRequestDto, + MentorUserRegisterRequestDto, + MentorVerifyRequestDto, + MentorDetailQueryDto, + ProfessionalProfile, + MentoringLogistics, + MentoringRate, + IdentityAndVerification, + MentorInsertDto, +}; + +// Export service and repository for internal use +pub use mentors_service::MentorsService; +pub use mentors_repository::MentorsRepository; + +// Export schema types for database interactions +pub use mentors_schema::MentorSchema; pub fn mentors_router() -> Router { Router::new() diff --git a/imphnen-dimentorin/src/v1/mod.rs b/imphnen-dimentorin/src/v1/mod.rs index 7cdb6f5..ebf1be4 100644 --- a/imphnen-dimentorin/src/v1/mod.rs +++ b/imphnen-dimentorin/src/v1/mod.rs @@ -2,6 +2,16 @@ use axum::Router; pub mod mentors; +/// Creates the main Dimentorin router with all version 1 endpoints +/// Routes: +/// - /mentors -> mentors::mentors_router() pub fn dimentorin_router() -> Router { - Router::new().nest("/mentors", mentors::mentors_router()) + Router::new() + .nest("/mentors", mentors::mentors_router()) } + +// Explicitly re-export key items for easier consumption +pub use mentors::mentors_router; +pub use mentors::MentorsService; +pub use mentors::MentorsRepository; +pub use mentors::MentorSchema; diff --git a/imphnen-dimentorin/src/v2/mod.rs b/imphnen-dimentorin/src/v2/mod.rs index 8b13789..22ab598 100644 --- a/imphnen-dimentorin/src/v2/mod.rs +++ b/imphnen-dimentorin/src/v2/mod.rs @@ -1 +1,14 @@ +/// Version 2 of the Dimentorin API - currently under development +/// This module will contain all version 2 endpoints following API versioning best practices +use axum::Router; + +/// Placeholder for version 2 router +/// To be implemented when version 2 endpoints are ready +pub fn dimentorin_v2_router() -> Router { + Router::new() + // Version 2 endpoints will be added here following the same pattern as v1 +} + +// Re-export the v1 router for backward compatibility +pub use crate::v1::dimentorin_router; diff --git a/imphnen-entities/src/lib.rs b/imphnen-entities/src/lib.rs index 63a456a..614ff49 100644 --- a/imphnen-entities/src/lib.rs +++ b/imphnen-entities/src/lib.rs @@ -2,7 +2,26 @@ pub mod common_dto; pub mod error_dto; pub mod users; pub mod permissions; -pub use common_dto::*; -pub use error_dto::*; -pub use users::*; -pub use permissions::*; + +// Re-export error type at root level for convenience +pub use error_dto::error::Error; + +// Explicit common_dto exports +pub use common_dto::CountResult; +pub use common_dto::MessageResponseDto; +pub use common_dto::MetaRequestDto; +pub use common_dto::MetaResponseDto; +pub use common_dto::ResponseListSuccessDto; +pub use common_dto::ResponseSuccessDto; + +// Explicit users exports +pub use users::EducationDto; +pub use users::ExperienceDto; +pub use users::RolesDetailItemDto; +pub use users::RolesDetailQueryDto; +pub use users::UsersDetailQueryDto; + +// Explicit permissions exports +pub use permissions::PermissionsEnum; +pub use permissions::PermissionsItemDto; +pub use permissions::PermissionsQueryDto; diff --git a/imphnen-gacha/src/lib.rs b/imphnen-gacha/src/lib.rs index 92b7daa..b7bd2cf 100644 --- a/imphnen-gacha/src/lib.rs +++ b/imphnen-gacha/src/lib.rs @@ -1,6 +1,49 @@ pub mod v1; -pub use imphnen_entities::*; -pub use imphnen_libs::*; -pub use imphnen_utils::*; -pub use v1::*; +// Re-export core entity types used across the gacha system +pub use imphnen_entities::{ + CountResult, + Error, + ExperienceDto, + EducationDto, + MessageResponseDto, + MetaRequestDto, + MetaResponseDto, + PermissionsEnum, + PermissionsItemDto, + PermissionsQueryDto, + ResponseListSuccessDto, + ResponseSuccessDto, + UsersDetailQueryDto, +}; + +// Explicitly import only what we need from libs and utils to avoid pollution +pub use imphnen_libs::{ + AppState, + MinioService, +}; + +pub use imphnen_utils::{ + bind_filter, + csrf_token, + extract_email, + generate_date, + generate_otp, + get_id, + logger, + make_thing, + mock_test, + query_builder, + query_list, + response_format, + serde_helpers, + validator, +}; + +// Re-export public v1 API +pub use v1::{ + gacha_claim_router, + gacha_item_router, + gacha_roll_router, + gacha_router, +}; diff --git a/imphnen-gacha/src/v1/gacha_claims/gacha_claims_controller.rs b/imphnen-gacha/src/v1/gacha_claims/gacha_claims_controller.rs index 554e93d..96c89dc 100644 --- a/imphnen-gacha/src/v1/gacha_claims/gacha_claims_controller.rs +++ b/imphnen-gacha/src/v1/gacha_claims/gacha_claims_controller.rs @@ -3,9 +3,9 @@ use axum::http::HeaderMap; use axum::response::IntoResponse; use axum::{Json, extract::Path}; use imphnen_iam::{PermissionsEnum, permissions_guard}; -use imphnen_libs::{AppState, MessageResponseDto, ResponseSuccessDto}; - -use super::{GachaClaimItemDto, GachaClaimRequestDto, GachaClaimService}; +use crate::AppState; +use imphnen_entities::{MessageResponseDto, ResponseSuccessDto}; +use crate::v1::gacha_claims::{GachaClaimItemDto, GachaClaimRequestDto, GachaClaimService}; #[utoipa::path( get, diff --git a/imphnen-gacha/src/v1/gacha_claims/gacha_claims_dto.rs b/imphnen-gacha/src/v1/gacha_claims/gacha_claims_dto.rs index 5570ada..19f5115 100644 --- a/imphnen-gacha/src/v1/gacha_claims/gacha_claims_dto.rs +++ b/imphnen-gacha/src/v1/gacha_claims/gacha_claims_dto.rs @@ -1,4 +1,5 @@ -use crate::{GachaItemDto, GachaItemSchema}; +use crate::v1::gacha_items::GachaItemDto; +use crate::v1::gacha_items::gacha_items_schema::GachaItemSchema; use imphnen_iam::{UsersDetailItemDto, UsersDetailQueryDto}; use serde::{Deserialize, Serialize}; use surrealdb::sql::Thing; diff --git a/imphnen-gacha/src/v1/gacha_claims/gacha_claims_repository.rs b/imphnen-gacha/src/v1/gacha_claims/gacha_claims_repository.rs index b8975b2..da60824 100644 --- a/imphnen-gacha/src/v1/gacha_claims/gacha_claims_repository.rs +++ b/imphnen-gacha/src/v1/gacha_claims/gacha_claims_repository.rs @@ -1,5 +1,7 @@ -use super::{GachaClaimQueryDto, GachaClaimSchema}; -use crate::{AppState, ResourceEnum}; +use crate::v1::gacha_claims::gacha_claims_dto::GachaClaimQueryDto; +use crate::v1::gacha_claims::gacha_claims_schema::GachaClaimSchema; +use crate::AppState; +use imphnen_libs::ResourceEnum; use anyhow::{Result, bail}; use imphnen_iam::DetailQueryBuilder; use std::time::Instant; diff --git a/imphnen-gacha/src/v1/gacha_claims/gacha_claims_schema.rs b/imphnen-gacha/src/v1/gacha_claims/gacha_claims_schema.rs index 0494ce1..b08d9ae 100644 --- a/imphnen-gacha/src/v1/gacha_claims/gacha_claims_schema.rs +++ b/imphnen-gacha/src/v1/gacha_claims/gacha_claims_schema.rs @@ -1,9 +1,11 @@ -use crate::{GachaRollQueryDto, ResourceEnum, make_thing}; +use crate::v1::gacha_rolls::gacha_rolls_dto::GachaRollQueryDto; +use crate::{make_thing}; use imphnen_iam::get_iso_date; +use imphnen_libs::ResourceEnum; use serde::{Deserialize, Serialize}; use surrealdb::{Uuid, sql::Thing}; -use super::GachaClaimRequestDto; +use crate::v1::gacha_claims::gacha_claims_dto::GachaClaimRequestDto; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct GachaClaimSchema { diff --git a/imphnen-gacha/src/v1/gacha_claims/gacha_claims_service.rs b/imphnen-gacha/src/v1/gacha_claims/gacha_claims_service.rs index b6d07cf..3b9d583 100644 --- a/imphnen-gacha/src/v1/gacha_claims/gacha_claims_service.rs +++ b/imphnen-gacha/src/v1/gacha_claims/gacha_claims_service.rs @@ -1,8 +1,9 @@ -use crate::{ - AppState, GachaClaimItemDto, GachaClaimRepository, GachaClaimRequestDto, - GachaClaimSchema, ResponseSuccessDto, common_response, success_response, - validate_request, -}; +use crate::AppState; +use imphnen_entities::ResponseSuccessDto; +use imphnen_utils::{common_response, success_response, validate_request}; +use crate::v1::gacha_claims::gacha_claims_dto::{GachaClaimItemDto, GachaClaimRequestDto}; +use crate::v1::gacha_claims::gacha_claims_repository::GachaClaimRepository; +use crate::v1::gacha_claims::gacha_claims_schema::GachaClaimSchema; use axum::http::StatusCode; use axum::response::Response; diff --git a/imphnen-gacha/src/v1/gacha_claims/mod.rs b/imphnen-gacha/src/v1/gacha_claims/mod.rs index bbce6b4..8870dbf 100644 --- a/imphnen-gacha/src/v1/gacha_claims/mod.rs +++ b/imphnen-gacha/src/v1/gacha_claims/mod.rs @@ -1,6 +1,6 @@ use axum::{ - Router, - routing::{get, post}, + Router, + routing::{get, post}, }; pub mod gacha_claims_controller; @@ -9,14 +9,14 @@ pub mod gacha_claims_repository; pub mod gacha_claims_schema; pub mod gacha_claims_service; -pub use gacha_claims_controller::*; -pub use gacha_claims_dto::*; -pub use gacha_claims_repository::*; -pub use gacha_claims_schema::*; -pub use gacha_claims_service::*; +// Export only public API functions +pub use gacha_claims_controller::{post_create_gacha_claim, get_detail_gacha_claim}; +pub use gacha_claims_dto::{GachaClaimItemDto, GachaClaimRequestDto}; +pub use gacha_claims_service::GachaClaimService; +/// Creates router for gacha claims endpoints pub fn gacha_claim_router() -> Router { - Router::new() - .route("/create", post(post_create_gacha_claim)) - .route("/detail/{id}", get(get_detail_gacha_claim)) + Router::new() + .route("/create", post(post_create_gacha_claim)) + .route("/detail/{id}", get(get_detail_gacha_claim)) } diff --git a/imphnen-gacha/src/v1/gacha_credits/gacha_credits_repository.rs b/imphnen-gacha/src/v1/gacha_credits/gacha_credits_repository.rs index 7719af6..c1b1470 100644 --- a/imphnen-gacha/src/v1/gacha_credits/gacha_credits_repository.rs +++ b/imphnen-gacha/src/v1/gacha_credits/gacha_credits_repository.rs @@ -1,5 +1,7 @@ -use super::{GachaCreditRequestDto, GachaCreditSchema}; -use crate::{AppState, ResourceEnum}; +use crate::v1::gacha_credits::gacha_credits_dto::GachaCreditRequestDto; +use crate::v1::gacha_credits::gacha_credits_schema::GachaCreditSchema; +use crate::AppState; +use imphnen_libs::ResourceEnum; use anyhow::{Result, bail}; use imphnen_iam::make_thing; use std::time::Instant; diff --git a/imphnen-gacha/src/v1/gacha_credits/mod.rs b/imphnen-gacha/src/v1/gacha_credits/mod.rs index 49d397b..6b825d1 100644 --- a/imphnen-gacha/src/v1/gacha_credits/mod.rs +++ b/imphnen-gacha/src/v1/gacha_credits/mod.rs @@ -2,6 +2,5 @@ pub mod gacha_credits_dto; pub mod gacha_credits_repository; pub mod gacha_credits_schema; -pub use gacha_credits_dto::*; -pub use gacha_credits_repository::*; -pub use gacha_credits_schema::*; +// Export only public types and functions +pub use gacha_credits_dto::GachaCreditRequestDto; diff --git a/imphnen-gacha/src/v1/gacha_items/gacha_items_controller.rs b/imphnen-gacha/src/v1/gacha_items/gacha_items_controller.rs index ac13f0b..760bdd2 100644 --- a/imphnen-gacha/src/v1/gacha_items/gacha_items_controller.rs +++ b/imphnen-gacha/src/v1/gacha_items/gacha_items_controller.rs @@ -1,7 +1,8 @@ -use crate::{ - AppState, GachaItemDto, GachaItemRequestDto, GachaItemUpdateRequestDto, GachaItemService, MessageResponseDto, - MetaRequestDto, ResponseListSuccessDto, ResponseSuccessDto, -}; +use crate::{AppState, MetaRequestDto, ResponseListSuccessDto, ResponseSuccessDto}; +use imphnen_entities::MessageResponseDto; +use crate::v1::gacha_items::GachaItemDto; +use crate::v1::gacha_items::gacha_items_dto::{GachaItemRequestDto, GachaItemUpdateRequestDto}; +use crate::v1::gacha_items::gacha_items_service::GachaItemService; use axum::{ Extension, Json, extract::{Path, Query}, diff --git a/imphnen-gacha/src/v1/gacha_items/gacha_items_dto.rs b/imphnen-gacha/src/v1/gacha_items/gacha_items_dto.rs index efd31f1..9429750 100644 --- a/imphnen-gacha/src/v1/gacha_items/gacha_items_dto.rs +++ b/imphnen-gacha/src/v1/gacha_items/gacha_items_dto.rs @@ -1,4 +1,4 @@ -use super::GachaItemSchema; +use crate::v1::gacha_items::gacha_items_schema::GachaItemSchema; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; use validator::Validate; diff --git a/imphnen-gacha/src/v1/gacha_items/gacha_items_repository.rs b/imphnen-gacha/src/v1/gacha_items/gacha_items_repository.rs index e7226cb..b4e20f6 100644 --- a/imphnen-gacha/src/v1/gacha_items/gacha_items_repository.rs +++ b/imphnen-gacha/src/v1/gacha_items/gacha_items_repository.rs @@ -1,8 +1,7 @@ -use super::GachaItemSchema; -use crate::{ - AppState, GachaItemDto, MetaRequestDto, ResourceEnum, ResponseListSuccessDto, - get_id, make_thing, -}; +use crate::v1::gacha_items::gacha_items_schema::GachaItemSchema; +use crate::{AppState, MetaRequestDto, ResponseListSuccessDto, get_id, make_thing}; +use crate::v1::gacha_items::GachaItemDto; +use imphnen_libs::ResourceEnum; use anyhow::{Result, bail}; use imphnen_iam::QueryListBuilder; use imphnen_utils::get_iso_date; diff --git a/imphnen-gacha/src/v1/gacha_items/gacha_items_schema.rs b/imphnen-gacha/src/v1/gacha_items/gacha_items_schema.rs index bdfd216..4ec5b0b 100644 --- a/imphnen-gacha/src/v1/gacha_items/gacha_items_schema.rs +++ b/imphnen-gacha/src/v1/gacha_items/gacha_items_schema.rs @@ -1,9 +1,10 @@ -use crate::{ResourceEnum, make_thing}; +use crate::make_thing; use imphnen_iam::get_iso_date; +use imphnen_libs::ResourceEnum; use serde::{Deserialize, Serialize}; use surrealdb::{Uuid, sql::Thing}; -use super::GachaItemRequestDto; +use crate::v1::gacha_items::gacha_items_dto::GachaItemRequestDto; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct GachaItemSchema { diff --git a/imphnen-gacha/src/v1/gacha_items/gacha_items_service.rs b/imphnen-gacha/src/v1/gacha_items/gacha_items_service.rs index 05aabfe..0c006ab 100644 --- a/imphnen-gacha/src/v1/gacha_items/gacha_items_service.rs +++ b/imphnen-gacha/src/v1/gacha_items/gacha_items_service.rs @@ -1,9 +1,11 @@ -use crate::{ - AppState, GachaItemDto, GachaItemRepository, GachaItemRequestDto, GachaItemUpdateRequestDto, GachaItemSchema, - MetaRequestDto, ResourceEnum, ResponseListSuccessDto, ResponseSuccessDto, - common_response, make_thing, success_list_response, success_response, - validate_request, -}; +use crate::AppState; +use imphnen_entities::{MetaRequestDto, ResponseListSuccessDto, ResponseSuccessDto}; +use imphnen_utils::{common_response, make_thing, success_list_response, success_response, validate_request}; +use crate::v1::gacha_items::GachaItemDto; +use crate::v1::gacha_items::gacha_items_dto::{GachaItemRequestDto, GachaItemUpdateRequestDto}; +use crate::v1::gacha_items::gacha_items_repository::GachaItemRepository; +use crate::v1::gacha_items::gacha_items_schema::GachaItemSchema; +use imphnen_libs::ResourceEnum; use axum::http::StatusCode; use axum::response::Response; use imphnen_utils::get_iso_date; diff --git a/imphnen-gacha/src/v1/gacha_items/mod.rs b/imphnen-gacha/src/v1/gacha_items/mod.rs index 50bc387..af9ce85 100644 --- a/imphnen-gacha/src/v1/gacha_items/mod.rs +++ b/imphnen-gacha/src/v1/gacha_items/mod.rs @@ -1,6 +1,6 @@ use axum::{ - Router, - routing::{delete, get, post, put}, + Router, + routing::{delete, get, post, put}, }; pub mod gacha_items_controller; @@ -9,17 +9,22 @@ pub mod gacha_items_repository; pub mod gacha_items_schema; pub mod gacha_items_service; -pub use gacha_items_controller::*; -pub use gacha_items_dto::*; -pub use gacha_items_repository::*; -pub use gacha_items_schema::*; -pub use gacha_items_service::*; +// Export only public API functions and types +pub use gacha_items_controller::{ + get_gacha_item_list, + post_create_gacha_item, + get_gacha_item_by_id, + put_update_gacha_item, + delete_gacha_item, +}; +pub use gacha_items_dto::GachaItemDto; +/// Creates router for gacha items endpoints pub fn gacha_item_router() -> Router { - Router::new() - .route("/", get(get_gacha_item_list)) - .route("/create", post(post_create_gacha_item)) - .route("/detail/{id}", get(get_gacha_item_by_id)) - .route("/update/{id}", put(put_update_gacha_item)) - .route("/delete/{id}", delete(delete_gacha_item)) + Router::new() + .route("/", get(get_gacha_item_list)) + .route("/create", post(post_create_gacha_item)) + .route("/detail/{id}", get(get_gacha_item_by_id)) + .route("/update/{id}", put(put_update_gacha_item)) + .route("/delete/{id}", delete(delete_gacha_item)) } diff --git a/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_controller.rs b/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_controller.rs index ad509e6..9485e25 100644 --- a/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_controller.rs +++ b/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_controller.rs @@ -1,7 +1,7 @@ -use crate::{ - AppState, GachaRollItemDto, GachaRollRequestDto, GachaRollService, - MessageResponseDto, ResponseSuccessDto, -}; +use crate::AppState; +use imphnen_entities::{MessageResponseDto, ResponseSuccessDto}; +use crate::v1::gacha_rolls::gacha_rolls_dto::{GachaRollItemDto, GachaRollRequestDto}; +use crate::v1::gacha_rolls::gacha_rolls_service::GachaRollService; use axum::{ Extension, Json, extract::Path, http::HeaderMap, response::IntoResponse, }; diff --git a/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_dto.rs b/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_dto.rs index d27cee3..15afe80 100644 --- a/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_dto.rs +++ b/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_dto.rs @@ -1,4 +1,5 @@ -use crate::{GachaItemDto, GachaItemSchema}; +use crate::v1::gacha_items::GachaItemDto; +use crate::v1::gacha_items::gacha_items_schema::GachaItemSchema; use serde::{Deserialize, Serialize}; use surrealdb::sql::Thing; use utoipa::ToSchema; diff --git a/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_repository.rs b/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_repository.rs index 4e8d238..a8445c8 100644 --- a/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_repository.rs +++ b/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_repository.rs @@ -1,6 +1,9 @@ -use super::GachaRollQueryDto; -use super::GachaRollSchema; -use crate::{AppState, DetailQueryBuilder, ResourceEnum, get_id, make_thing}; +use crate::v1::gacha_rolls::gacha_rolls_dto::GachaRollQueryDto; +use crate::v1::gacha_rolls::gacha_rolls_schema::GachaRollSchema; +use crate::AppState; +use imphnen_libs::ResourceEnum; +use imphnen_utils::DetailQueryBuilder; +use crate::{get_id, make_thing}; use anyhow::{Result, bail}; use rand::prelude::*; diff --git a/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_schema.rs b/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_schema.rs index a97006f..7b31a27 100644 --- a/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_schema.rs +++ b/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_schema.rs @@ -1,9 +1,10 @@ -use crate::{ResourceEnum, make_thing}; +use crate::make_thing; use imphnen_iam::get_iso_date; +use imphnen_libs::ResourceEnum; use serde::{Deserialize, Serialize}; use surrealdb::{Uuid, sql::Thing}; -use super::GachaRollRequestDto; +use crate::v1::gacha_rolls::gacha_rolls_dto::GachaRollRequestDto; #[derive(Clone, Debug, Serialize, Deserialize)] pub struct GachaRollSchema { diff --git a/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_service.rs b/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_service.rs index a3cb17d..1214bc7 100644 --- a/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_service.rs +++ b/imphnen-gacha/src/v1/gacha_rolls/gacha_rolls_service.rs @@ -1,11 +1,15 @@ -use crate::{ - AppState, GachaClaimRepository, GachaClaimSchema, GachaRollItemDto, - GachaRollRepository, GachaRollRequestDto, GachaRollSchema, ResponseSuccessDto, - common_response, success_response, validate_request, -}; +use crate::AppState; +use imphnen_entities::ResponseSuccessDto; +use imphnen_utils::{common_response, success_response, validate_request}; +use crate::v1::gacha_claims::gacha_claims_repository::GachaClaimRepository; +use crate::v1::gacha_claims::gacha_claims_schema::GachaClaimSchema; +use crate::v1::gacha_rolls::gacha_rolls_dto::{GachaRollItemDto, GachaRollRequestDto}; +use crate::v1::gacha_rolls::gacha_rolls_repository::GachaRollRepository; +use crate::v1::gacha_rolls::gacha_rolls_schema::GachaRollSchema; use axum::http::{HeaderMap, StatusCode}; use axum::response::Response; -use imphnen_iam::{UsersRepository, extract_email}; +use imphnen_iam::UsersRepository; +use imphnen_utils::extract_email; pub struct GachaRollService; diff --git a/imphnen-gacha/src/v1/gacha_rolls/mod.rs b/imphnen-gacha/src/v1/gacha_rolls/mod.rs index e102f99..94cac24 100644 --- a/imphnen-gacha/src/v1/gacha_rolls/mod.rs +++ b/imphnen-gacha/src/v1/gacha_rolls/mod.rs @@ -1,22 +1,26 @@ +use axum::{ + Router, + routing::{get, post}, +}; + pub mod gacha_rolls_controller; pub mod gacha_rolls_dto; pub mod gacha_rolls_repository; pub mod gacha_rolls_schema; pub mod gacha_rolls_service; -use axum::{ - Router, - routing::{get, post}, +// Export only public API functions and types +pub use gacha_rolls_controller::{ + post_create_gacha_roll, + post_execute_gacha_roll, + get_detail_gacha_roll, }; -pub use gacha_rolls_controller::*; -pub use gacha_rolls_dto::*; -pub use gacha_rolls_repository::*; -pub use gacha_rolls_schema::*; -pub use gacha_rolls_service::*; +pub use gacha_rolls_dto::GachaRollItemDto; +/// Creates router for gacha rolls endpoints pub fn gacha_roll_router() -> Router { - Router::new() - .route("/create", post(post_create_gacha_roll)) - .route("/execute", post(post_execute_gacha_roll)) - .route("/detail/{id}", get(get_detail_gacha_roll)) + Router::new() + .route("/create", post(post_create_gacha_roll)) + .route("/execute", post(post_execute_gacha_roll)) + .route("/detail/{id}", get(get_detail_gacha_roll)) } diff --git a/imphnen-gacha/src/v1/mod.rs b/imphnen-gacha/src/v1/mod.rs index f310eb3..e14277b 100644 --- a/imphnen-gacha/src/v1/mod.rs +++ b/imphnen-gacha/src/v1/mod.rs @@ -5,14 +5,16 @@ pub mod gacha_credits; pub mod gacha_items; pub mod gacha_rolls; -pub use gacha_claims::*; -pub use gacha_credits::*; -pub use gacha_items::*; -pub use gacha_rolls::*; +// Export only public router functions to avoid namespace pollution +pub use gacha_claims::gacha_claim_router; +pub use gacha_credits::*; // gacha_credits doesn't have router functions +pub use gacha_items::gacha_item_router; +pub use gacha_rolls::gacha_roll_router; +/// Creates the main gacha router with all version 1 endpoints pub fn gacha_router() -> Router { - Router::new() - .nest("/gacha/claims", gacha_claim_router()) - .nest("/gacha/items", gacha_item_router()) - .nest("/gacha/rolls", gacha_roll_router()) + Router::new() + .nest("/claims", gacha_claim_router()) + .nest("/items", gacha_item_router()) + .nest("/rolls", gacha_roll_router()) } diff --git a/imphnen-gateway/src/docs.rs b/imphnen-gateway/src/docs.rs index 5d02670..ddc1cb8 100644 --- a/imphnen-gateway/src/docs.rs +++ b/imphnen-gateway/src/docs.rs @@ -1,11 +1,9 @@ -use imphnen_cms::{ - events_controller, - events_dto::{EventsDetailItemDto, EventsListItemDto}, - testimonials_controller, - testimonials_dto::{ - TestimonialsCreateRequestDto, TestimonialsDetailItemDto, - TestimonialsListItemDto, TestimonialsUpdateRequestDto, - }, +use imphnen_cms::v1::landing::events::events_controller; +use imphnen_cms::v1::landing::events::events_dto::{EventsDetailItemDto, EventsListItemDto}; +use imphnen_cms::v1::landing::testimonials::testimonials_controller; +use imphnen_cms::v1::landing::testimonials::testimonials_dto::{ + TestimonialsCreateRequestDto, TestimonialsDetailItemDto, + TestimonialsListItemDto, TestimonialsUpdateRequestDto, }; use imphnen_dimentorin::v1::mentors::{ mentors_controller, @@ -16,21 +14,20 @@ use imphnen_dimentorin::v1::mentors::{ MentoringLogistics, MentoringRate, ProfessionalProfile, }, }; -use imphnen_gacha::{ - GachaClaimItemDto, GachaClaimRequestDto, GachaItemDto, GachaItemRequestDto, - GachaRollItemDto, GachaRollRequestDto, gacha_claims, gacha_items, gacha_rolls, -}; +use imphnen_gacha::v1::gacha_claims::{gacha_claims_controller, GachaClaimItemDto, GachaClaimRequestDto}; +use imphnen_gacha::v1::gacha_items::{gacha_items_controller, GachaItemDto}; +use imphnen_gacha::v1::gacha_items::gacha_items_dto::GachaItemRequestDto; +use imphnen_gacha::v1::gacha_rolls::{gacha_rolls_controller, GachaRollItemDto}; +use imphnen_gacha::v1::gacha_rolls::gacha_rolls_dto::GachaRollRequestDto; use imphnen_entities::{PermissionsItemDto, RolesDetailItemDto}; -use imphnen_iam::{ - AuthLoginRequestDto, AuthLoginResponsetDto, AuthNewPasswordRequestDto, - AuthRefreshTokenRequestDto, AuthResendOtpRequestDto, AuthVerifyEmailRequestDto, - MessageResponseDto, MetaRequestDto, MetaResponseDto, PermissionsRequestDto, - ResponseListSuccessDto, ResponseSuccessDto, RolesListItemDto, RolesRequestCreateDto, - RolesRequestUpdateDto, TokenDto, UsersCreateRequestDto, UsersDetailItemDto, - UsersListItemDto, UsersUpdateRequestDto, TeamsCreateRequestDto, TeamsUpdateRequestDto, - TeamInviteRequestDto, TeamAcceptInvitationRequestDto, TeamsDetailItemDto, - TeamsListItemDto, TeamMemberDto, TeamInvitationDto, TeamsSearchQueryDto, -}; +use imphnen_entities::{MessageResponseDto, MetaRequestDto, MetaResponseDto, ResponseListSuccessDto, ResponseSuccessDto}; +use imphnen_iam::v1::auth::auth_dto::{AuthLoginRequestDto, AuthLoginResponsetDto, AuthNewPasswordRequestDto, AuthRefreshTokenRequestDto, AuthResendOtpRequestDto, AuthVerifyEmailRequestDto, TokenDto}; +use imphnen_iam::v1::permissions::permissions_dto::PermissionsRequestDto; +use imphnen_iam::v1::roles::RolesListItemDto; +use imphnen_iam::v1::roles::roles_dto::{RolesRequestCreateDto, RolesRequestUpdateDto}; +use imphnen_iam::v1::users::UsersDetailItemDto; +use imphnen_iam::v1::users::users_dto::{UsersCreateRequestDto, UsersListItemDto, UsersUpdateRequestDto}; +use imphnen_iam::v1::teams::teams_dto::{TeamsCreateRequestDto, TeamsUpdateRequestDto, TeamInviteRequestDto, TeamAcceptInvitationRequestDto, TeamsDetailItemDto, TeamsListItemDto, TeamMemberDto, TeamInvitationDto, TeamsSearchQueryDto}; use imphnen_iam::v1::{auth, permissions, roles, users, teams}; use imphnen_iam::v1::users::users_controller::FileUploadSchema; use utoipa::{ @@ -78,16 +75,16 @@ use utoipa::{ teams::teams_controller::get_public_team_search, teams::teams_controller::get_team_members, teams::teams_controller::post_leave_team, - gacha_claims::get_detail_gacha_claim, - gacha_claims::post_create_gacha_claim, - gacha_items::get_gacha_item_list, - gacha_items::get_gacha_item_by_id, - gacha_items::post_create_gacha_item, - gacha_items::put_update_gacha_item, - gacha_items::delete_gacha_item, - gacha_rolls::get_detail_gacha_roll, - gacha_rolls::post_create_gacha_roll, - gacha_rolls::post_execute_gacha_roll, + gacha_claims_controller::get_detail_gacha_claim, + gacha_claims_controller::post_create_gacha_claim, + gacha_items_controller::get_gacha_item_list, + gacha_items_controller::get_gacha_item_by_id, + gacha_items_controller::post_create_gacha_item, + gacha_items_controller::put_update_gacha_item, + gacha_items_controller::delete_gacha_item, + gacha_rolls_controller::get_detail_gacha_roll, + gacha_rolls_controller::post_create_gacha_roll, + gacha_rolls_controller::post_execute_gacha_roll, events_controller::get_event_list, events_controller::get_event_by_id, events_controller::post_create_event, @@ -209,7 +206,7 @@ use utoipa::{ pub struct ApiDoc; -struct SecurityAddon; +pub struct SecurityAddon; impl Modify for SecurityAddon { fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) { diff --git a/imphnen-gateway/src/lib.rs b/imphnen-gateway/src/lib.rs index 0bc8dd4..fe90d1b 100644 --- a/imphnen-gateway/src/lib.rs +++ b/imphnen-gateway/src/lib.rs @@ -1,51 +1,60 @@ use axum::{ - Extension, Router, middleware::from_fn, response::Redirect, routing::get, + Extension, + Router, + middleware::from_fn, + response::Redirect, + routing::get, }; use imphnen_cms::{ - events_protected_routes, events_public_routes, testimonials_protected_routes, - testimonials_public_routes, + events_protected_routes, + events_public_routes, + testimonials_protected_routes, + testimonials_public_routes, }; use imphnen_dimentorin::dimentorin_router; -use imphnen_libs::{AppState, SurrealMemClient, SurrealWsClient}; use imphnen_gacha::gacha_router; -use imphnen_iam::{iam_protected_routes, iam_public_routes, v1::users::users_service::UsersService, v1::auth::auth_repository::AuthRepoImpl}; +use imphnen_iam::{ + iam_protected_routes, + iam_public_routes, + v1::users::users_service::UsersService, + v1::auth::auth_repository::AuthRepoImpl, +}; +use imphnen_libs::{AppState, SurrealMemClient, SurrealWsClient}; use imphnen_middleware::{auth_middleware, cors_middleware}; use std::sync::Arc; use utoipa_swagger_ui::SwaggerUi; pub mod docs; -pub use docs::*; +pub use docs::{ApiDoc, SecurityAddon, docs_router}; pub async fn gateway_service( - surrealdb_ws: SurrealWsClient, - surrealdb_mem: SurrealMemClient, + surrealdb_ws: SurrealWsClient, + surrealdb_mem: SurrealMemClient, ) -> Router { - let state = AppState { - surrealdb_ws, - surrealdb_mem: surrealdb_mem.clone(), - user_lookup_service: Arc::new(UsersService), - auth_repository: Arc::new(AuthRepoImpl { db: surrealdb_mem }), - }; + let state = AppState { + surrealdb_ws, + surrealdb_mem: surrealdb_mem.clone(), + user_lookup_service: Arc::new(UsersService), + auth_repository: Arc::new(AuthRepoImpl { db: surrealdb_mem }), + }; - let public_routes = Router::new() - .merge(iam_public_routes()) - .merge(testimonials_public_routes()) - .merge(events_public_routes()); + let public_routes = Router::new() + .merge(iam_public_routes()) + .merge(testimonials_public_routes()) + .merge(events_public_routes()); - let protected_routes = Router::new() - .merge(iam_protected_routes()) - .merge(events_protected_routes()) - .merge(testimonials_protected_routes()) - .merge(dimentorin_router()) - .merge(gacha_router()) - .layer(from_fn(auth_middleware)); + let protected_routes = Router::new() + .merge(iam_protected_routes()) + .merge(events_protected_routes()) + .merge(testimonials_protected_routes()) + .merge(dimentorin_router()) + .merge(gacha_router()) + .layer(from_fn(auth_middleware)); - let routes = public_routes.merge(protected_routes); - - Router::new() - .route("/", get(Redirect::to("/docs"))) - .nest("/v1", routes) - .merge(SwaggerUi::new("/docs").url("/openapi.json", docs_router())) - .layer(cors_middleware()) - .layer(Extension(state)) + Router::new() + .route("/", get(Redirect::to("/docs"))) + .nest("/v1", public_routes.merge(protected_routes)) + .merge(SwaggerUi::new("/docs").url("/openapi.json", docs_router())) + .layer(cors_middleware()) + .layer(Extension(state)) } diff --git a/imphnen-iam/src/lib.rs b/imphnen-iam/src/lib.rs index 92b7daa..1873fd8 100644 --- a/imphnen-iam/src/lib.rs +++ b/imphnen-iam/src/lib.rs @@ -1,6 +1,93 @@ pub mod v1; -pub use imphnen_entities::*; -pub use imphnen_libs::*; -pub use imphnen_utils::*; -pub use v1::*; +// Re-export core entity types used throughout the IAM module +pub use imphnen_entities::{ + MessageResponseDto, + MetaRequestDto, + MetaResponseDto, + ResponseSuccessDto, + ResponseListSuccessDto, + CountResult, + Error, + ExperienceDto, + EducationDto, + UsersDetailQueryDto, + PermissionsEnum, + PermissionsItemDto, + PermissionsQueryDto, +}; + +// Explicitly export only the imphnen_libs types actually used in IAM +pub use imphnen_libs::{ + AppState, + ResourceEnum, + decode_access_token, + decode_refresh_token, + encode_access_token, + encode_refresh_token, + encode_reset_password_token, + hash_password, + send_email, + verify_password, + Env, + SurrealWsClient, + SurrealMemClient, + UserLookupService, + AuthRepositoryTrait, + jsonwebtoken::Claims, +}; + +// Explicitly export only the imphnen_utils types actually used in IAM +pub use imphnen_utils::{ + make_thing, + make_thing_from_enum, + get_id, + get_iso_date, + extract_id, + build_multi_thing_condition, + execute_safe_update_query, + DetailQueryBuilder, + QueryListBuilder, + success_response, + success_list_response, + common_response, + validate_request, + generate_oauth_csrf_token, + validate_oauth_csrf_token, + validate_csrf_token, + extract_email_token_async, + OtpManager, +}; + +// Export the main router functions and types from v1 module +pub use v1::{ + iam_public_routes, + iam_protected_routes, + auth_router, + users_router, + roles_router, + permissions_router, + teams_router, + permissions_guard, +}; + +// Export IAM-specific types +pub use v1::auth::{ + AuthRepository, AuthOtpSchema, + AuthLoginRequestDto, AuthLoginResponsetDto, AuthRegisterRequestDto, + AuthResendOtpRequestDto, AuthVerifyEmailRequestDto, + AuthNewPasswordRequestDto, AuthRefreshTokenRequestDto, + TokenDto, UserCacheSchema, +}; +pub use v1::permissions::{PermissionsRepository, PermissionsSchema}; +pub use v1::roles::{RolesRepository, RolesSchema, RolesEnum, RolesDetailQueryDto, RolesRequestCreateDto, RolesRequestUpdateDto, RolesDetailItemDto}; +pub use v1::teams::{ + TeamsRepository, TeamsSchema, TeamsCreateRequestDto, TeamsUpdateRequestDto, + TeamInviteRequestDto, TeamMemberDto, AdminTeamsListItemDto, + AdminTeamsDetailItemDto, TeamsDetailItemDto, TeamsListItemDto, + TeamAcceptInvitationRequestDto, TeamsSearchQueryDto, PublicTeamsListItemDto, + PublicTeamsDetailItemDto, TeamsDetailQueryDto, TeamsListQueryDto, + TeamMembersSchema, TeamInvitationsSchema, TeamMembersQueryDto, + TeamInvitationsQueryDto, MemberTeamsDetailItemDto, +}; +pub use v1::users::{UsersRepository, UsersSchema, UsersDetailItemDto, UsersCreateRequestDto}; diff --git a/imphnen-iam/src/v1/auth/auth_controller.rs b/imphnen-iam/src/v1/auth/auth_controller.rs index 05be6cb..fced3be 100644 --- a/imphnen-iam/src/v1/auth/auth_controller.rs +++ b/imphnen-iam/src/v1/auth/auth_controller.rs @@ -2,7 +2,7 @@ use super::{ AuthLoginRequestDto, AuthRefreshTokenRequestDto, AuthRegisterRequestDto, AuthResendOtpRequestDto, AuthVerifyEmailRequestDto, }; -use crate::{AppState, v1::AuthLoginResponsetDto}; +use crate::{AppState, v1::auth::AuthLoginResponsetDto}; use crate::{AuthNewPasswordRequestDto, MessageResponseDto, ResponseSuccessDto}; use axum::{Extension, Json, response::IntoResponse}; use crate::v1::auth::auth_service::AuthServiceTrait; diff --git a/imphnen-iam/src/v1/auth/auth_service.rs b/imphnen-iam/src/v1/auth/auth_service.rs index b188e12..19a1d60 100644 --- a/imphnen-iam/src/v1/auth/auth_service.rs +++ b/imphnen-iam/src/v1/auth/auth_service.rs @@ -1,5 +1,7 @@ use std::pin::Pin; use std::future::Future; +use imphnen_utils as generate_otp; +use imphnen_libs::enviroment; use super::{ AuthLoginRequestDto, AuthLoginResponsetDto, AuthNewPasswordRequestDto, AuthRefreshTokenRequestDto, AuthRegisterRequestDto, AuthRepository, @@ -9,7 +11,7 @@ use crate::{ AppState, ResourceEnum, ResponseSuccessDto, RolesEnum, RolesRepository, UsersDetailItemDto, UsersRepository, UsersSchema, common_response, decode_refresh_token, encode_access_token, encode_refresh_token, - encode_reset_password_token, extract_email_token_async, generate_otp, get_iso_date, + encode_reset_password_token, extract_email_token_async, get_iso_date, hash_password, make_thing, send_email, success_response, validate_request, verify_password, }; @@ -475,7 +477,7 @@ impl AuthServiceTrait for AuthService { } }; - let env = &crate::enviroment::ENV; + let env = &enviroment::ENV; let fe_url = env.fe_url.clone(); let message = format!( "You have requested a password reset. Please click the link below to continue: {fe_url}/auth/reset-password?token={token}" diff --git a/imphnen-iam/src/v1/auth/google/mod.rs b/imphnen-iam/src/v1/auth/google/mod.rs index 8b71daa..21029ad 100644 --- a/imphnen-iam/src/v1/auth/google/mod.rs +++ b/imphnen-iam/src/v1/auth/google/mod.rs @@ -1,3 +1,7 @@ +/// Google OAuth integration module pub mod google_oauth_controller; pub mod google_oauth_dto; -pub mod google_oauth_service; \ No newline at end of file +pub mod google_oauth_service; + +// Export only essential types and functions from Google OAuth submodules +pub use google_oauth_controller::GoogleOauthController; \ No newline at end of file diff --git a/imphnen-iam/src/v1/auth/mod.rs b/imphnen-iam/src/v1/auth/mod.rs index 7ed810d..e999b65 100644 --- a/imphnen-iam/src/v1/auth/mod.rs +++ b/imphnen-iam/src/v1/auth/mod.rs @@ -7,20 +7,45 @@ pub mod auth_schema; pub mod auth_service; pub mod google; -pub use auth_dto::*; -pub use auth_repository::*; -pub use auth_schema::*; -pub use auth_service::*; +// Export only the essential types and functions from each submodule +pub use auth_dto::{ + AuthLoginRequestDto, + AuthLoginResponsetDto, + AuthRegisterRequestDto, + AuthResendOtpRequestDto, + AuthVerifyEmailRequestDto, + AuthNewPasswordRequestDto, + AuthRefreshTokenRequestDto, + TokenDto, + UserCacheSchema, +}; + +pub use auth_repository::AuthRepository; +pub use imphnen_libs::AuthRepositoryTrait; +pub use auth_schema::AuthOtpSchema; +pub use auth_service::AuthServiceTrait; + +// Export controller functions that are used in routing +pub use auth_controller::{ + post_login, + post_login_mentor, + post_register, + post_forgot_password, + post_new_password, + post_refresh_token, + post_resend_otp, + post_verify_email +}; pub fn auth_router() -> Router { Router::new() .nest("/google", google::google_oauth_controller::GoogleOauthController::new().get_routes()) - .route("/forgot", post(auth_controller::post_forgot_password)) - .route("/login", post(auth_controller::post_login)) - .route("/login-mentor", post(auth_controller::post_login_mentor)) - .route("/new-password", post(auth_controller::post_new_password)) - .route("/refresh", post(auth_controller::post_refresh_token)) - .route("/register", post(auth_controller::post_register)) - .route("/send-otp", post(auth_controller::post_resend_otp)) - .route("/verify-email", post(auth_controller::post_verify_email)) + .route("/forgot", post(post_forgot_password)) + .route("/login", post(post_login)) + .route("/login-mentor", post(post_login_mentor)) + .route("/new-password", post(post_new_password)) + .route("/refresh", post(post_refresh_token)) + .route("/register", post(post_register)) + .route("/send-otp", post(post_resend_otp)) + .route("/verify-email", post(post_verify_email)) } diff --git a/imphnen-iam/src/v1/mod.rs b/imphnen-iam/src/v1/mod.rs index 2bd0ad0..9963a94 100644 --- a/imphnen-iam/src/v1/mod.rs +++ b/imphnen-iam/src/v1/mod.rs @@ -6,12 +6,14 @@ pub mod roles; pub mod teams; pub mod users; -pub use auth::*; -pub use permissions::*; -pub use roles::*; -pub use teams::*; -pub use users::*; +// Export only the essential router functions from each module +pub use auth::auth_router; +pub use permissions::{permissions_router, permissions_dto, permissions_service, permissions_guard}; +pub use roles::{roles_router, roles_service}; +pub use teams::teams_router; +pub use users::users_router; +// Main route constructors pub fn iam_public_routes() -> Router { Router::new().nest("/auth", auth_router()) } diff --git a/imphnen-iam/src/v1/permissions/mod.rs b/imphnen-iam/src/v1/permissions/mod.rs index c8628fb..b6756e0 100644 --- a/imphnen-iam/src/v1/permissions/mod.rs +++ b/imphnen-iam/src/v1/permissions/mod.rs @@ -10,12 +10,24 @@ pub mod permissions_repository; pub mod permissions_schema; pub mod permissions_service; -pub use permissions_controller::*; -pub use permissions_dto::*; -pub use permissions_enum::*; -pub use permissions_guard::*; -pub use permissions_repository::*; -pub use permissions_schema::*; +// Export only essential types and functions from each submodule +pub use permissions_controller::{ + get_permission_list, + get_permission_by_id, + post_create_permission, + put_update_permission, + delete_permission +}; + +pub use permissions_dto::{ + PermissionsRequestDto, + PermissionsUpdateRequestDto, +}; + +pub use permissions_enum::PermissionsEnum; +pub use permissions_guard::permissions_guard; +pub use permissions_repository::PermissionsRepository; +pub use permissions_schema::PermissionsSchema; pub fn permissions_router() -> Router { Router::new() diff --git a/imphnen-iam/src/v1/roles/mod.rs b/imphnen-iam/src/v1/roles/mod.rs index 0da30a3..cee782f 100644 --- a/imphnen-iam/src/v1/roles/mod.rs +++ b/imphnen-iam/src/v1/roles/mod.rs @@ -10,12 +10,26 @@ pub mod roles_repository; pub mod roles_schema; pub mod roles_service; -pub use roles_controller::*; -pub use roles_dto::*; -pub use roles_enum::*; -pub use roles_repository::*; -pub use roles_schema::*; -pub use roles_service::*; +// Export only essential types and functions from each submodule +pub use roles_controller::{ + get_role_list, + get_role_by_id, + post_create_role, + put_update_role, + delete_role +}; + +pub use roles_dto::{ + RolesRequestCreateDto, + RolesRequestUpdateDto, + RolesDetailItemDto, + RolesListItemDto, + RolesDetailQueryDto, +}; + +pub use roles_enum::RolesEnum; +pub use roles_repository::RolesRepository; +pub use roles_schema::RolesSchema; pub fn roles_router() -> Router { Router::new() diff --git a/imphnen-iam/src/v1/teams/mod.rs b/imphnen-iam/src/v1/teams/mod.rs index 0ef43d1..63f6f47 100644 --- a/imphnen-iam/src/v1/teams/mod.rs +++ b/imphnen-iam/src/v1/teams/mod.rs @@ -5,15 +5,50 @@ pub mod teams_repository; pub mod teams_schema; pub mod teams_service; -pub use admin_teams_controller::{admin_teams_router, get_all_teams as admin_get_all_teams, get_team_by_id as admin_get_team_by_id, get_team_members as admin_get_team_members, create_team as admin_create_team, update_team as admin_update_team, delete_team as admin_delete_team, invite_team_members as admin_invite_team_members}; -pub use teams_controller::{teams_router as user_teams_router, get_team_list, get_team_by_id as user_get_team_by_id, get_team_members as user_get_team_members}; -pub use teams_dto::*; -pub use teams_repository::*; -pub use teams_schema::*; -pub use teams_service::*; - use axum::Router; +// Export only essential types and functions from each submodule +pub use admin_teams_controller::{ + admin_teams_router, + get_all_teams as admin_get_all_teams, + get_team_by_id as admin_get_team_by_id, + get_team_members as admin_get_team_members, + create_team as admin_create_team, + update_team as admin_update_team, + delete_team as admin_delete_team, + invite_team_members as admin_invite_team_members +}; + +pub use teams_controller::{ + teams_router as user_teams_router, + get_team_list, + get_team_by_id as user_get_team_by_id, + get_team_members as user_get_team_members +}; + +pub use teams_dto::{ + TeamsCreateRequestDto, + TeamsUpdateRequestDto, + TeamInviteRequestDto, + TeamMemberDto, + TeamsListItemDto, + TeamsDetailItemDto, + PublicTeamsListItemDto, + PublicTeamsDetailItemDto, + AdminTeamsListItemDto, + AdminTeamsDetailItemDto, + TeamAcceptInvitationRequestDto, + TeamsSearchQueryDto, + TeamsDetailQueryDto, + TeamsListQueryDto, + TeamMembersQueryDto, + TeamInvitationsQueryDto, + MemberTeamsDetailItemDto +}; + +pub use teams_repository::TeamsRepository; +pub use teams_schema::{TeamsSchema, TeamMembersSchema, TeamInvitationsSchema}; + pub fn teams_router() -> Router { Router::new() // Public routes diff --git a/imphnen-iam/src/v1/users/mod.rs b/imphnen-iam/src/v1/users/mod.rs index fd945b4..9a0d8a6 100644 --- a/imphnen-iam/src/v1/users/mod.rs +++ b/imphnen-iam/src/v1/users/mod.rs @@ -9,11 +9,31 @@ pub mod users_repository; pub mod users_schema; pub mod users_service; -pub use users_controller::*; -pub use users_dto::*; -pub use users_repository::*; -pub use users_schema::*; -pub use users_service::*; +// Export only essential types and functions from each submodule +pub use users_controller::{ + get_user_list, + get_user_by_id, + get_user_me, + post_create_user, + put_update_user, + put_update_user_me, + delete_user, + patch_user_active_status, + upload_file +}; + +pub use users_dto::{ + UsersActiveInactiveRequestDto, + UsersCreateRequestDto, + UsersUpdateRequestDto, + UsersSetNewPasswordRequestDto, + UsersDetailItemDto, + UsersListItemDto, + UsersListQueryDto, +}; + +pub use users_repository::UsersRepository; +pub use users_schema::UsersSchema; pub fn users_router() -> Router { Router::new() diff --git a/imphnen-libs/src/argon/mod.rs b/imphnen-libs/src/argon/mod.rs index 84fe0af..7caabe9 100644 --- a/imphnen-libs/src/argon/mod.rs +++ b/imphnen-libs/src/argon/mod.rs @@ -1,29 +1,83 @@ +//! Argon2 password hashing utilities. +//! +//! This module provides secure password hashing and verification using the Argon2 algorithm. +//! The hashing parameters are configured for a balance between security and performance. + use argon2::{ - Argon2, - password_hash::{ - Error, PasswordHash, PasswordHasher, PasswordVerifier, SaltString, - rand_core::OsRng, - }, + Argon2, + password_hash::{ + Error, PasswordHash, PasswordHasher, PasswordVerifier, SaltString, + rand_core::OsRng, + }, }; +// Configuration constants for Argon2 hashing +const MEMORY_COST: u32 = 1024; // 1MB +const TIME_COST: u32 = 1; // 1 iteration +const PARALLELISM: u32 = 1; // 1 thread + +/// Hash a password using Argon2id algorithm. +/// +/// This function generates a cryptographically secure salt and hashes the password +/// with predefined parameters optimized for a balance of security and performance. +/// +/// # Arguments +/// * `password` - The plain text password to hash +/// +/// # Returns +/// * `Ok(String)` - The hashed password in PHC string format +/// * `Err(Error)` - If hashing fails +/// +/// # Example +/// ``` +/// use imphnen_libs::hash_password; +/// +/// let hash = hash_password("my_password")?; +/// assert!(hash.starts_with("$argon2id$")); +/// # Ok::<(), argon2::password_hash::Error>(()) +/// ``` pub fn hash_password(password: &str) -> Result { - let salt = SaltString::generate(&mut OsRng); - let argon2 = Argon2::new( - argon2::Algorithm::Argon2id, - argon2::Version::V0x13, - argon2::Params::new(1024, 1, 1, None).unwrap() // 1MB, 1 iteration, 1 thread (faster, less secure) - ); - let password_hash = argon2 - .hash_password(password.as_bytes(), &salt)? - .to_string(); - Ok(password_hash) + let salt = SaltString::generate(&mut OsRng); + let argon2 = Argon2::new( + argon2::Algorithm::Argon2id, + argon2::Version::V0x13, + argon2::Params::new(MEMORY_COST, TIME_COST, PARALLELISM, None).unwrap(), + ); + + let password_hash = argon2 + .hash_password(password.as_bytes(), &salt)? + .to_string(); + Ok(password_hash) } +/// Verify a password against its hash. +/// +/// This function checks if the provided password matches the given hash. +/// Returns false for both incorrect passwords and invalid hash formats. +/// +/// # Arguments +/// * `password` - The plain text password to verify +/// * `hash` - The hashed password in PHC string format +/// +/// # Returns +/// * `Ok(bool)` - true if password matches, false otherwise +/// * `Err(Error)` - If hash parsing fails +/// +/// # Example +/// ``` +/// use imphnen_libs::{hash_password, verify_password}; +/// +/// let hash = hash_password("my_password")?; +/// assert!(verify_password("my_password", &hash)?); +/// assert!(!verify_password("wrong_password", &hash)?); +/// # Ok::<(), argon2::password_hash::Error>(()) +/// ``` pub fn verify_password(password: &str, hash: &str) -> Result { - let parsed_hash = PasswordHash::new(hash)?; - let argon2 = Argon2::default(); - match argon2.verify_password(password.as_bytes(), &parsed_hash) { - Ok(_) => Ok(true), - Err(_) => Ok(false), - } + let parsed_hash = PasswordHash::new(hash)?; + let argon2 = Argon2::default(); + + match argon2.verify_password(password.as_bytes(), &parsed_hash) { + Ok(_) => Ok(true), + Err(_) => Ok(false), + } } diff --git a/imphnen-libs/src/axum/mod.rs b/imphnen-libs/src/axum/mod.rs index 700e971..88db171 100644 --- a/imphnen-libs/src/axum/mod.rs +++ b/imphnen-libs/src/axum/mod.rs @@ -1,27 +1,80 @@ +//! Axum server initialization utilities. +//! +//! This module provides utilities for initializing and running an Axum web server +//! with SurrealDB connections for both WebSocket and in-memory databases. + use crate::{surrealdb_init_mem, surrealdb_init_ws, SurrealMemClient, SurrealWsClient}; use axum::{Router, serve}; use std::{future::Future, net::SocketAddr}; use tokio::net::TcpListener; use crate::enviroment::ENV; +/// Initialize and start the Axum server with SurrealDB connections. +/// +/// This function sets up both WebSocket and in-memory SurrealDB connections, +/// builds the router using the provided function, and starts the server. +/// +/// # Arguments +/// * `router_fn` - A function that takes SurrealDB clients and returns a Router +/// +/// # Panics +/// This function will panic if: +/// - SurrealDB initialization fails +/// - TCP listener binding fails +/// +/// # Example +/// ```no_run +/// use axum::Router; +/// use imphnen_libs::{axum_init, SurrealWsClient, SurrealMemClient}; +/// +/// async fn create_router(ws: SurrealWsClient, mem: SurrealMemClient) -> Router { +/// Router::new() +/// // Add your routes here +/// } +/// +/// #[tokio::main] +/// async fn main() { +/// axum_init(create_router).await; +/// } +/// ``` pub async fn axum_init(router_fn: F) where - F: FnOnce(SurrealWsClient, SurrealMemClient) -> Fut, - Fut: Future, + F: FnOnce(SurrealWsClient, SurrealMemClient) -> Fut, + Fut: Future, { - let env = &ENV; + let env = &ENV; - let surrealdb_ws = surrealdb_init_ws().await.expect("Failed surrealdb ws"); + // Initialize SurrealDB connections + log::info!("Initializing SurrealDB connections..."); + let surrealdb_ws = surrealdb_init_ws() + .await + .expect("Failed to initialize SurrealDB WebSocket connection"); - let surrealdb_mem = surrealdb_init_mem().await.expect("Failed surrealdb mem"); + let surrealdb_mem = surrealdb_init_mem() + .await + .expect("Failed to initialize SurrealDB in-memory connection"); - let router = router_fn(surrealdb_ws, surrealdb_mem).await; + log::info!("SurrealDB connections established successfully"); - let port = env.port; - let addr = SocketAddr::from(([0, 0, 0, 0], port)); - let listener = TcpListener::bind(&addr).await.unwrap(); + // Build the router + let router = router_fn(surrealdb_ws, surrealdb_mem).await; - if let Err(err) = serve(listener, router).await { - log::error!("Server failed to start: {}", err); - } + // Start the server + let port = env.port; + let addr = SocketAddr::from(([0, 0, 0, 0], port)); + log::info!("Starting server on {}", addr); + + let listener = TcpListener::bind(&addr) + .await + .unwrap_or_else(|e| { + log::error!("Failed to bind to address {}: {}", addr, e); + panic!("Server binding failed: {}", e); + }); + + log::info!("Server listening on {}", addr); + + if let Err(err) = serve(listener, router).await { + log::error!("Server encountered an error: {}", err); + panic!("Server failed: {}", err); + } } diff --git a/imphnen-libs/src/enviroment/mod.rs b/imphnen-libs/src/enviroment/mod.rs index 2740933..9dad7d7 100644 --- a/imphnen-libs/src/enviroment/mod.rs +++ b/imphnen-libs/src/enviroment/mod.rs @@ -1,11 +1,16 @@ //! Environment configuration module using once_cell::sync::Lazy for one-time loading. +//! +//! This module provides centralized configuration management for the application. +//! All environment variables are loaded once at startup and cached for performance. use std::env; use once_cell::sync::Lazy; -// Logging for warnings if .env is missing use log::{warn, info}; /// Struct holding all environment configuration. +/// +/// This struct contains all configuration values loaded from environment variables. +/// Sensitive values are masked in debug output for security. #[derive(Clone)] pub struct Env { pub port: u16, @@ -16,7 +21,6 @@ pub struct Env { pub surrealdb_password: String, pub surrealdb_namespace: String, pub surrealdb_dbname: String, - pub surrealdb_url_ws: String, pub smtp_email: String, pub smtp_password: String, pub smtp_name: String, @@ -48,7 +52,6 @@ impl std::fmt::Debug for Env { .field("surrealdb_password", &"***") .field("surrealdb_namespace", &self.surrealdb_namespace) .field("surrealdb_dbname", &self.surrealdb_dbname) - .field("surrealdb_url_ws", &self.surrealdb_url_ws) .field("smtp_email", &self.smtp_email) .field("smtp_password", &"***") .field("smtp_name", &self.smtp_name) @@ -69,7 +72,17 @@ impl std::fmt::Debug for Env { } } -/// Helper to get env var with warning if not set. +/// Get environment variable with warning if not set. +/// +/// This helper function attempts to read an environment variable and logs a warning +/// if it's not set, falling back to the provided default value. +/// +/// # Arguments +/// * `key` - The environment variable name +/// * `default` - The default value to use if the variable is not set +/// +/// # Returns +/// The environment variable value or the default fn get_env_with_warning(key: &str, default: &str) -> String { match env::var(key) { Ok(val) => val, @@ -80,49 +93,113 @@ fn get_env_with_warning(key: &str, default: &str) -> String { } } -/// Loads environment variables from .env and system, only once. -pub static ENV: Lazy = Lazy::new(|| { - // Try to load .env file, log a warning if not found, proceed regardless. - match dotenvy::dotenv() { - Ok(_) => {} - Err(dotenvy::Error::Io(ref e)) if e.kind() == std::io::ErrorKind::NotFound => { - warn!(".env file not found, falling back to system environment variables"); +/// Parse environment variable as u16 with fallback. +/// +/// # Arguments +/// * `key` - The environment variable name +/// * `default` - The default numeric value +/// +/// # Returns +/// The parsed u16 value or the default if parsing fails +fn get_env_u16_with_warning(key: &str, default: u16) -> u16 { + match env::var(key) { + Ok(val) => val.parse().unwrap_or_else(|_| { + warn!("Environment variable '{}' has invalid value '{}'. Using default: {}", key, val, default); + default + }), + Err(_) => { + warn!("Environment variable '{}' is not set. Using default: {}", key, default); + default } - Err(_) => {} } +} + +/// Parse environment variable as bool with fallback. +/// +/// # Arguments +/// * `key` - The environment variable name +/// * `default` - The default boolean value +/// +/// # Returns +/// The parsed boolean value or the default if parsing fails +fn get_env_bool_with_warning(key: &str, default: bool) -> bool { + match env::var(key) { + Ok(val) => val.parse().unwrap_or_else(|_| { + warn!("Environment variable '{}' has invalid value '{}'. Using default: {}", key, val, default); + default + }), + Err(_) => { + warn!("Environment variable '{}' is not set. Using default: {}", key, default); + default + } + } +} + +/// Global environment configuration loaded once at startup. +/// +/// This static variable loads all environment configuration exactly once +/// and caches it for the lifetime of the application. +pub static ENV: Lazy = Lazy::new(|| { + // Load .env file if present + load_dotenv_file(); let env = Env { - port: get_env_with_warning("PORT", "3000") - .parse() - .unwrap_or(3000), + // Server configuration + port: get_env_u16_with_warning("PORT", 3000), + + // JWT secrets access_token_secret: get_env_with_warning("ACCESS_TOKEN_SECRET", "default_access_secret"), refresh_token_secret: get_env_with_warning("REFRESH_TOKEN_SECRET", "default_refresh_secret"), + + // SurrealDB configuration surrealdb_url: get_env_with_warning("SURREALDB_URL", "http://localhost:8000"), surrealdb_username: get_env_with_warning("SURREALDB_USERNAME", "root"), surrealdb_password: get_env_with_warning("SURREALDB_PASSWORD", "root"), surrealdb_namespace: get_env_with_warning("SURREALDB_NAMESPACE", "namespace"), surrealdb_dbname: get_env_with_warning("SURREALDB_DBNAME", "database"), + + // SMTP configuration smtp_email: get_env_with_warning("SMTP_EMAIL", "no-reply@example.com"), smtp_password: get_env_with_warning("SMTP_PASSWORD", "default_smtp_password"), smtp_name: get_env_with_warning("SMTP_NAME", "MyApp SMTP"), smtp_host: get_env_with_warning("SMTP_HOST", "smtp.gmail.com"), + + // Redis configuration redisdb_url: get_env_with_warning("REDISDB_URL", "localhost"), + + // Frontend URL fe_url: get_env_with_warning("FE_URL", "http://localhost"), + + // Environment rust_env: get_env_with_warning("RUST_ENV", "development"), + + // MinIO configuration minio_endpoint: get_env_with_warning("MINIO_ENDPOINT", "http://localhost:9000"), minio_bucket_name: get_env_with_warning("MINIO_BUCKET_NAME", "imphnen-uploads"), minio_access_key: get_env_with_warning("MINIO_ACCESS_KEY", "minio_access"), minio_secret_key: get_env_with_warning("MINIO_SECRET_KEY", "minio_secret"), minio_region: get_env_with_warning("MINIO_REGION", "us-east-1"), - minio_secure: get_env_with_warning("MINIO_SECURE", "false") - .parse() - .unwrap_or(false), - surrealdb_url_ws: String::new(), + minio_secure: get_env_bool_with_warning("MINIO_SECURE", false), + // Google OAuth 2.1 google_client_id: get_env_with_warning("GOOGLE_CLIENT_ID", "default_google_client_id"), google_client_secret: get_env_with_warning("GOOGLE_CLIENT_SECRET", "default_google_client_secret"), google_redirect_url: get_env_with_warning("GOOGLE_REDIRECT_URL", "http://localhost:8000/api/v1/auth/google/callback"), }; - info!("Loaded environment configuration: {:?}", env); + + info!("Environment configuration loaded successfully"); env }); + +/// Load .env file if present, with appropriate logging. +fn load_dotenv_file() { + match dotenvy::dotenv() { + Ok(path) => info!("Loaded environment file: {:?}", path), + Err(dotenvy::Error::Io(ref e)) if e.kind() == std::io::ErrorKind::NotFound => { + warn!(".env file not found, falling back to system environment variables"); + } + Err(e) => { + warn!("Failed to load .env file: {}. Falling back to system environment variables", e); + } + } +} diff --git a/imphnen-libs/src/jsonwebtoken/mod.rs b/imphnen-libs/src/jsonwebtoken/mod.rs index 3f8f106..d4e7f3e 100644 --- a/imphnen-libs/src/jsonwebtoken/mod.rs +++ b/imphnen-libs/src/jsonwebtoken/mod.rs @@ -1,99 +1,161 @@ +//! JWT token encoding and decoding utilities. +//! +//! This module provides functions for creating and validating JWT tokens +//! for authentication purposes, including access tokens, refresh tokens, +//! and password reset tokens. + use crate::enviroment::ENV; use axum::http::StatusCode; use chrono::{Duration, TimeDelta, Utc}; use jsonwebtoken::{ - DecodingKey, EncodingKey, Header, TokenData, Validation, decode, encode, + DecodingKey, EncodingKey, Header, TokenData, Validation, decode, encode, }; use serde::{Deserialize, Serialize}; +/// JWT claims structure containing token payload information. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Claims { - pub exp: usize, - pub iat: usize, - pub sub: String, + /// Expiration timestamp + pub exp: usize, + /// Issued at timestamp + pub iat: usize, + /// Subject (usually user identifier) + pub sub: String, + /// User ID pub user_id: String, } +// Token configuration constants +const ACCESS_TOKEN_DURATION_MINUTES: i64 = 15; +const REFRESH_TOKEN_DURATION_DAYS: i64 = 1; +const RESET_TOKEN_DURATION_MINUTES: i64 = 5; + +// Lazy-initialized headers and keys for performance static ACCESS_HEADER: once_cell::sync::Lazy
= once_cell::sync::Lazy::new(Header::default); static ACCESS_KEY: once_cell::sync::Lazy = once_cell::sync::Lazy::new(|| { - EncodingKey::from_secret(ENV.access_token_secret.as_ref()) + EncodingKey::from_secret(ENV.access_token_secret.as_ref()) }); -pub fn encode_access_token(sub: String, user_id: String) -> Result { - let now = Utc::now(); - let expire: TimeDelta = Duration::minutes(15); - let exp: usize = (now + expire).timestamp() as usize; - let iat: usize = now.timestamp() as usize; - let claim = Claims { iat, exp, sub, user_id }; - encode( - &ACCESS_HEADER, - &claim, - &ACCESS_KEY, - ) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) -} - -pub fn encode_reset_password_token(sub: String, user_id: String) -> Result { - let env = &ENV; - let secret: String = env.access_token_secret.clone(); - let now = Utc::now(); - let expire: TimeDelta = Duration::minutes(5); - let exp: usize = (now + expire).timestamp() as usize; - let iat: usize = now.timestamp() as usize; - let claim = Claims { iat, exp, sub, user_id }; - encode( - &Header::default(), - &claim, - &EncodingKey::from_secret(secret.as_ref()), - ) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) -} - -pub fn decode_access_token( - jwt_token: &str, -) -> Result, StatusCode> { - let env = &ENV; - let secret: String = env.access_token_secret.clone(); - let result: Result, StatusCode> = decode( - jwt_token, - &DecodingKey::from_secret(secret.as_ref()), - &Validation::default(), - ) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); - result -} static REFRESH_HEADER: once_cell::sync::Lazy
= once_cell::sync::Lazy::new(Header::default); static REFRESH_KEY: once_cell::sync::Lazy = once_cell::sync::Lazy::new(|| { - EncodingKey::from_secret(ENV.refresh_token_secret.as_ref()) + EncodingKey::from_secret(ENV.refresh_token_secret.as_ref()) }); + +/// Create JWT claims with specified expiration duration. +/// +/// # Arguments +/// * `sub` - Subject identifier +/// * `user_id` - User ID +/// * `duration` - Token validity duration +/// +/// # Returns +/// JWT claims structure +fn create_claims(sub: String, user_id: String, duration: TimeDelta) -> Claims { + let now = Utc::now(); + let exp: usize = (now + duration).timestamp() as usize; + let iat: usize = now.timestamp() as usize; + Claims { iat, exp, sub, user_id } +} + +/// Encode a JWT token with the specified header and key. +/// +/// # Arguments +/// * `claims` - JWT claims to encode +/// * `header` - JWT header +/// * `key` - Encoding key +/// +/// # Returns +/// Encoded JWT token or internal server error status +fn encode_token(claims: &Claims, header: &Header, key: &EncodingKey) -> Result { + encode(header, claims, key).map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) +} + +/// Decode a JWT token with the specified secret. +/// +/// # Arguments +/// * `token` - JWT token string +/// * `secret` - Secret key for decoding +/// +/// # Returns +/// Decoded token data or internal server error status +fn decode_token(token: &str, secret: &str) -> Result, StatusCode> { + decode( + token, + &DecodingKey::from_secret(secret.as_ref()), + &Validation::default(), + ) + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) +} + +/// Encode an access token with 15-minute expiration. +/// +/// # Arguments +/// * `sub` - Subject identifier +/// * `user_id` - User ID +/// +/// # Returns +/// Encoded JWT access token +pub fn encode_access_token(sub: String, user_id: String) -> Result { + let claims = create_claims(sub, user_id, Duration::minutes(ACCESS_TOKEN_DURATION_MINUTES)); + encode_token(&claims, &ACCESS_HEADER, &ACCESS_KEY) +} + +/// Encode a refresh token with 1-day expiration. +/// +/// # Arguments +/// * `sub` - Subject identifier +/// * `user_id` - User ID +/// +/// # Returns +/// Encoded JWT refresh token pub fn encode_refresh_token(sub: String, user_id: String) -> Result { - let now = Utc::now(); - let expire: TimeDelta = Duration::days(1); - let exp: usize = (now + expire).timestamp() as usize; - let iat: usize = now.timestamp() as usize; - let claim = Claims { iat, exp, sub, user_id }; - encode( - &REFRESH_HEADER, - &claim, - &REFRESH_KEY, - ) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) + let claims = create_claims(sub, user_id, Duration::days(REFRESH_TOKEN_DURATION_DAYS)); + encode_token(&claims, &REFRESH_HEADER, &REFRESH_KEY) } -pub fn decode_refresh_token( - jwt_token: &str, -) -> Result, StatusCode> { - let env = &ENV; - let secret: String = env.refresh_token_secret.clone(); - let result: Result, StatusCode> = decode( - jwt_token, - &DecodingKey::from_secret(secret.as_ref()), - &Validation::default(), - ) - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR); - result // Explicitly return result +/// Encode a password reset token with 5-minute expiration. +/// +/// # Arguments +/// * `sub` - Subject identifier +/// * `user_id` - User ID +/// +/// # Returns +/// Encoded JWT reset token +pub fn encode_reset_password_token(sub: String, user_id: String) -> Result { + let claims = create_claims(sub, user_id, Duration::minutes(RESET_TOKEN_DURATION_MINUTES)); + let key = EncodingKey::from_secret(ENV.access_token_secret.as_ref()); + encode_token(&claims, &Header::default(), &key) } +/// Decode an access token. +/// +/// # Arguments +/// * `jwt_token` - JWT token string +/// +/// # Returns +/// Decoded token data containing claims +pub fn decode_access_token(jwt_token: &str) -> Result, StatusCode> { + decode_token(jwt_token, &ENV.access_token_secret) +} + +/// Decode a refresh token. +/// +/// # Arguments +/// * `jwt_token` - JWT token string +/// +/// # Returns +/// Decoded token data containing claims +pub fn decode_refresh_token(jwt_token: &str) -> Result, StatusCode> { + decode_token(jwt_token, &ENV.refresh_token_secret) +} + +/// Generate a simple JWT access token using user_id as both sub and user_id. +/// +/// # Arguments +/// * `user_id` - User identifier +/// +/// # Returns +/// Encoded JWT access token pub fn generate_jwt(user_id: &str) -> Result { encode_access_token(user_id.to_string(), user_id.to_string()) } diff --git a/imphnen-libs/src/lettre/mod.rs b/imphnen-libs/src/lettre/mod.rs index 059cff2..5d0bbfc 100644 --- a/imphnen-libs/src/lettre/mod.rs +++ b/imphnen-libs/src/lettre/mod.rs @@ -1,35 +1,120 @@ +//! Email sending utilities using Lettre SMTP client. +//! +//! This module provides functionality for sending emails through SMTP +//! with proper error handling and logging. + use crate::enviroment::ENV; use lettre::message::Mailbox; use lettre::transport::smtp::authentication::Credentials; use lettre::{Message, SmtpTransport, Transport}; use std::error::Error; +use std::fmt; -pub fn send_email( - to: &str, - subject: &str, - body: &str, -) -> Result<(), Box> { - let env = &ENV; - let host = env.smtp_host.clone(); - let sender_email = env.smtp_email.clone(); - let sender_name = env.smtp_name.clone(); - let sender_password = env.smtp_password.clone(); - let recipient_email = to; - let email = Message::builder() - .from(Mailbox::new( - Some(sender_name.replace("-", " ")), - sender_email.parse()?, - )) - .to(recipient_email.parse()?) - .subject(subject) - .body(body.to_string())?; - let smtp_credentials = - Credentials::new(sender_email, sender_password.replace("-", " ")); - let mailer = SmtpTransport::relay(&host)? - .credentials(smtp_credentials) - .build(); - match mailer.send(&email) { - Ok(_) => Ok(()), - Err(e) => Err(Box::new(e)), - } +/// Custom error type for email operations. +#[derive(Debug)] +pub enum EmailError { + /// SMTP configuration error + SmtpConfig(String), + /// Message building error + MessageBuild(String), + /// SMTP transport error + Transport(String), +} + +impl fmt::Display for EmailError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + EmailError::SmtpConfig(msg) => write!(f, "SMTP configuration error: {}", msg), + EmailError::MessageBuild(msg) => write!(f, "Message building error: {}", msg), + EmailError::Transport(msg) => write!(f, "SMTP transport error: {}", msg), + } + } +} + +impl Error for EmailError {} + +/// Send an email using the configured SMTP settings. +/// +/// This function constructs and sends an email using the SMTP configuration +/// from environment variables. It handles sender name normalization and +/// proper error reporting. +/// +/// # Arguments +/// * `to` - Recipient email address +/// * `subject` - Email subject line +/// * `body` - Email body content (plain text) +/// +/// # Returns +/// * `Ok(())` - Email sent successfully +/// * `Err(EmailError)` - Email sending failed +/// +/// # Example +/// ``` +/// use imphnen_libs::send_email; +/// +/// send_email("user@example.com", "Welcome!", "Hello, welcome to our service!")?; +/// # Ok::<(), Box>(()) +/// ``` +pub fn send_email(to: &str, subject: &str, body: &str) -> Result<(), Box> { + let env = &ENV; + + // Build the email message + let message = build_email_message(to, subject, body, env)?; + + // Create SMTP transport + let mailer = create_smtp_transport(env)?; + + // Send the email + mailer.send(&message).map_err(|e| { + log::error!("Failed to send email to {}: {}", to, e); + Box::new(EmailError::Transport(e.to_string())) as Box + })?; + + log::info!("Email sent successfully to: {}", to); + Ok(()) +} + +/// Build an email message with proper sender and recipient configuration. +/// +/// # Arguments +/// * `to` - Recipient email address +/// * `subject` - Email subject +/// * `body` - Email body +/// * `env` - Environment configuration +/// +/// # Returns +/// Email message or error +fn build_email_message( + to: &str, + subject: &str, + body: &str, + env: &crate::enviroment::Env, +) -> Result> { + let sender_name = env.smtp_name.replace("-", " "); // Normalize sender name + + Message::builder() + .from(Mailbox::new(Some(sender_name), env.smtp_email.parse()?)) + .to(to.parse()?) + .subject(subject) + .body(body.to_string()) + .map_err(|e| Box::new(EmailError::MessageBuild(e.to_string())) as Box) +} + +/// Create SMTP transport with authentication. +/// +/// # Arguments +/// * `env` - Environment configuration +/// +/// # Returns +/// Configured SMTP transport or error +fn create_smtp_transport(env: &crate::enviroment::Env) -> Result> { + let credentials = Credentials::new( + env.smtp_email.clone(), + env.smtp_password.replace("-", " "), // Normalize password + ); + + Ok(SmtpTransport::relay(&env.smtp_host)? + .credentials(credentials) + .build()) + } diff --git a/imphnen-libs/src/lib.rs b/imphnen-libs/src/lib.rs index 30b70be..e456041 100644 --- a/imphnen-libs/src/lib.rs +++ b/imphnen-libs/src/lib.rs @@ -9,15 +9,35 @@ pub mod minio; pub mod services; pub mod surrealdb; -pub use argon::*; -pub use axum::*; -pub use enviroment::*; -pub use imphnen_entities::*; -pub use jsonwebtoken::*; -pub use lettre::*; -pub use minio::*; -pub use services::*; -pub use surrealdb::*; +pub use argon::{hash_password, verify_password}; +pub use axum::axum_init; +pub use enviroment::{ENV, Env}; +pub use imphnen_entities::{ + MessageResponseDto, + MetaRequestDto, + MetaResponseDto, + ResponseSuccessDto, + ResponseListSuccessDto, + CountResult, + Error, + ExperienceDto, + EducationDto, + UsersDetailQueryDto, + PermissionsEnum, + PermissionsItemDto, + PermissionsQueryDto, +}; +pub use jsonwebtoken::{ + Claims, encode_access_token, encode_refresh_token, decode_access_token, + decode_refresh_token, encode_reset_password_token, generate_jwt +}; +pub use lettre::send_email; +pub use minio::*; // Minio has many useful exports, keeping for now +pub use services::{UserLookupService, AuthRepositoryTrait}; +pub use surrealdb::{ + surrealdb_init_ws, surrealdb_init_mem, SurrealWsClient, SurrealMemClient, + ResourceEnum +}; #[derive(Clone)] pub struct AppState { diff --git a/imphnen-libs/src/surrealdb/mod.rs b/imphnen-libs/src/surrealdb/mod.rs index 21b3782..0681b5c 100644 --- a/imphnen-libs/src/surrealdb/mod.rs +++ b/imphnen-libs/src/surrealdb/mod.rs @@ -1,35 +1,105 @@ +//! SurrealDB client initialization and configuration. +//! +//! This module provides utilities for initializing SurrealDB connections +//! for both WebSocket and in-memory databases, along with resource definitions. + use crate::enviroment::ENV; use surrealdb::engine::any; use surrealdb::engine::local::{Db, Mem}; use surrealdb::opt::auth::Root; use surrealdb::{Result, Surreal}; +/// Type alias for SurrealDB WebSocket client. pub type SurrealWsClient = Surreal; + +/// Type alias for SurrealDB in-memory client. pub type SurrealMemClient = Surreal; pub mod resource; pub use resource::*; +/// Initialize a SurrealDB WebSocket client connection. +/// +/// This function creates a connection to a SurrealDB instance via WebSocket, +/// authenticates with root credentials, and sets the namespace and database. +/// +/// # Returns +/// * `Ok(SurrealWsClient)` - Successfully initialized WebSocket client +/// * `Err(surrealdb::Error)` - Connection, authentication, or configuration failed +/// +/// # Example +/// ```no_run +/// use imphnen_libs::surrealdb_init_ws; +/// +/// #[tokio::main] +/// async fn main() -> Result<(), Box> { +/// let client = surrealdb_init_ws().await?; +/// // Use client for database operations +/// Ok(()) +/// } +/// ``` pub async fn surrealdb_init_ws() -> Result> { - let env = &ENV; - let db = any::connect(&env.surrealdb_url).await?; + let env = &ENV; - db.signin(Root { - username: &env.surrealdb_username, - password: &env.surrealdb_password, - }) - .await?; - db.use_ns(env.surrealdb_namespace.clone()) - .use_db(env.surrealdb_dbname.clone()) - .await?; - Ok(db) + log::info!("Initializing SurrealDB WebSocket connection to: {}", env.surrealdb_url); + + // Connect to SurrealDB + let db = any::connect(&env.surrealdb_url).await?; + log::debug!("WebSocket connection established"); + + // Authenticate + db.signin(Root { + username: &env.surrealdb_username, + password: &env.surrealdb_password, + }) + .await?; + log::debug!("Authentication successful"); + + // Configure namespace and database + db.use_ns(&env.surrealdb_namespace) + .use_db(&env.surrealdb_dbname) + .await?; + log::info!("SurrealDB WebSocket client initialized with namespace '{}' and database '{}'", + env.surrealdb_namespace, env.surrealdb_dbname); + + Ok(db) } +/// Initialize a SurrealDB in-memory client. +/// +/// This function creates an in-memory SurrealDB instance and configures +/// the namespace and database for use. +/// +/// # Returns +/// * `Ok(SurrealMemClient)` - Successfully initialized in-memory client +/// * `Err(surrealdb::Error)` - Initialization or configuration failed +/// +/// # Example +/// ```no_run +/// use imphnen_libs::surrealdb_init_mem; +/// +/// #[tokio::main] +/// async fn main() -> Result<(), Box> { +/// let client = surrealdb_init_mem().await?; +/// // Use client for in-memory database operations +/// Ok(()) +/// } +/// ``` pub async fn surrealdb_init_mem() -> Result { - let env = &ENV; - let db = Surreal::new::(()).await?; - db.use_ns(&env.surrealdb_namespace) - .use_db(&env.surrealdb_dbname) - .await?; - Ok(db) + let env = &ENV; + + log::info!("Initializing SurrealDB in-memory database"); + + // Create in-memory database + let db = Surreal::new::(()).await?; + log::debug!("In-memory database created"); + + // Configure namespace and database + db.use_ns(&env.surrealdb_namespace) + .use_db(&env.surrealdb_dbname) + .await?; + log::info!("SurrealDB in-memory client initialized with namespace '{}' and database '{}'", + env.surrealdb_namespace, env.surrealdb_dbname); + + Ok(db) } diff --git a/imphnen-libs/src/surrealdb/resource.rs b/imphnen-libs/src/surrealdb/resource.rs index 543903c..55c2230 100644 --- a/imphnen-libs/src/surrealdb/resource.rs +++ b/imphnen-libs/src/surrealdb/resource.rs @@ -1,45 +1,138 @@ +//! SurrealDB resource definitions. +//! +//! This module defines the database table names used throughout the application. +//! Each resource corresponds to a SurrealDB table with the "app_" prefix. + use std::fmt; -#[derive(Debug, Clone, PartialEq, Eq)] +/// Database resource enumeration. +/// +/// Represents all database tables used in the application. +/// Each variant corresponds to a SurrealDB table name. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ResourceEnum { - OtpCache, - UsersCache, - GachaItems, - GachaClaims, - GachaRolls, - GachaCredits, - Users, - Roles, - Permissions, - RolesPermissions, - Events, - Testimonials, - Mentors, - Teams, - TeamMembers, - TeamInvitations, + /// OTP cache table for temporary authentication codes + OtpCache, + /// User cache table for user session data + UsersCache, + /// Gacha items table + GachaItems, + /// Gacha claims table for user item claims + GachaClaims, + /// Gacha rolls table for user roll history + GachaRolls, + /// Gacha credits table for user currency + GachaCredits, + /// Users table for user accounts + Users, + /// Roles table for user roles + Roles, + /// Permissions table for system permissions + Permissions, + /// Role-permission relationships table + RolesPermissions, + /// Events table for application events + Events, + /// Testimonials table for user testimonials + Testimonials, + /// Mentors table for mentor profiles + Mentors, + /// Teams table for user teams + Teams, + /// Team members table for team membership + TeamMembers, + /// Team invitations table for pending invitations + TeamInvitations, } impl fmt::Display for ResourceEnum { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let str = match self { - ResourceEnum::Users => "app_users", - ResourceEnum::UsersCache => "app_users_cache", - ResourceEnum::OtpCache => "app_otp_cache", - ResourceEnum::Roles => "app_roles", - ResourceEnum::Permissions => "app_permissions", - ResourceEnum::RolesPermissions => "app_roles_permissions", - ResourceEnum::GachaItems => "app_gacha_items", - ResourceEnum::GachaClaims => "app_gacha_claims", - ResourceEnum::GachaRolls => "app_gacha_rolls", - ResourceEnum::GachaCredits => "app_gacha_credits", - ResourceEnum::Events => "app_events", - ResourceEnum::Testimonials => "app_testimonials", - ResourceEnum::Mentors => "app_mentors", - ResourceEnum::Teams => "app_teams", - ResourceEnum::TeamMembers => "app_team_members", - ResourceEnum::TeamInvitations => "app_team_invitations", - }; - write!(f, "{str}") - } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let table_name = match self { + ResourceEnum::Users => "app_users", + ResourceEnum::UsersCache => "app_users_cache", + ResourceEnum::OtpCache => "app_otp_cache", + ResourceEnum::Roles => "app_roles", + ResourceEnum::Permissions => "app_permissions", + ResourceEnum::RolesPermissions => "app_roles_permissions", + ResourceEnum::GachaItems => "app_gacha_items", + ResourceEnum::GachaClaims => "app_gacha_claims", + ResourceEnum::GachaRolls => "app_gacha_rolls", + ResourceEnum::GachaCredits => "app_gacha_credits", + ResourceEnum::Events => "app_events", + ResourceEnum::Testimonials => "app_testimonials", + ResourceEnum::Mentors => "app_mentors", + ResourceEnum::Teams => "app_teams", + ResourceEnum::TeamMembers => "app_team_members", + ResourceEnum::TeamInvitations => "app_team_invitations", + }; + write!(f, "{}", table_name) + } +} + +impl ResourceEnum { + /// Get the table name as a string slice. + /// + /// # Returns + /// The SurrealDB table name for this resource + /// + /// # Example + /// ``` + /// use imphnen_libs::ResourceEnum; + /// + /// let users = ResourceEnum::Users; + /// assert_eq!(users.as_str(), "app_users"); + /// ``` + pub fn as_str(&self) -> &'static str { + match self { + ResourceEnum::Users => "app_users", + ResourceEnum::UsersCache => "app_users_cache", + ResourceEnum::OtpCache => "app_otp_cache", + ResourceEnum::Roles => "app_roles", + ResourceEnum::Permissions => "app_permissions", + ResourceEnum::RolesPermissions => "app_roles_permissions", + ResourceEnum::GachaItems => "app_gacha_items", + ResourceEnum::GachaClaims => "app_gacha_claims", + ResourceEnum::GachaRolls => "app_gacha_rolls", + ResourceEnum::GachaCredits => "app_gacha_credits", + ResourceEnum::Events => "app_events", + ResourceEnum::Testimonials => "app_testimonials", + ResourceEnum::Mentors => "app_mentors", + ResourceEnum::Teams => "app_teams", + ResourceEnum::TeamMembers => "app_team_members", + ResourceEnum::TeamInvitations => "app_team_invitations", + } + } + + /// Check if this resource is cache-related. + /// + /// # Returns + /// true if the resource is used for caching, false otherwise + pub fn is_cache(&self) -> bool { + matches!(self, ResourceEnum::OtpCache | ResourceEnum::UsersCache) + } + + /// Check if this resource is gacha-related. + /// + /// # Returns + /// true if the resource is part of the gacha system, false otherwise + pub fn is_gacha(&self) -> bool { + matches!( + self, + ResourceEnum::GachaItems + | ResourceEnum::GachaClaims + | ResourceEnum::GachaRolls + | ResourceEnum::GachaCredits + ) + } + + /// Check if this resource is user-related. + /// + /// # Returns + /// true if the resource contains user data, false otherwise + pub fn is_user_related(&self) -> bool { + matches!( + self, + ResourceEnum::Users | ResourceEnum::UsersCache | ResourceEnum::Mentors + ) + } } diff --git a/imphnen-middleware/src/auth_middleware/mod.rs b/imphnen-middleware/src/auth_middleware/mod.rs index 55c9648..6178644 100644 --- a/imphnen-middleware/src/auth_middleware/mod.rs +++ b/imphnen-middleware/src/auth_middleware/mod.rs @@ -41,28 +41,27 @@ pub async fn auth_middleware( // Try SurrealDB mem first let mem_db = &state.surrealdb_mem; - let mut user_data: Option = None; - if let Ok(opt_user) = mem_db.select(("users", &user_id)).await { - if let Some(user) = opt_user { - let user: UsersDetailQueryDto = user; - if !user.is_deleted && !user.role.is_deleted { - user_data = Some(user); - } + let user_data = if let Ok(Some(user)) = mem_db.select::>(("users", &user_id)).await { + if !user.is_deleted && !user.role.is_deleted { + Some(user) + } else { + None } - } + } else { + None + }; // Fallback to main DB if not found in mem - let user_data = match user_data { - Some(user) => user, - None => { - match state.user_lookup_service.get_user_by_id_internal(&thing_id, &state).await { - Ok(user) => { - // Optionally: insert into mem for future requests - let _: Result, _> = mem_db.update(("users", &user_id)).content(user.clone()).await; - user - }, - Err(_) => return Ok(common_response(StatusCode::UNAUTHORIZED, "User not found")), - } + let user_data = if let Some(user) = user_data { + user + } else { + match state.user_lookup_service.get_user_by_id_internal(&thing_id, &state).await { + Ok(user) => { + // Cache in mem for future requests + let _: Result, _> = mem_db.update(("users", &user_id)).content(user.clone()).await; + user + }, + Err(_) => return Ok(common_response(StatusCode::UNAUTHORIZED, "User not found")), } }; diff --git a/imphnen-middleware/src/lib.rs b/imphnen-middleware/src/lib.rs index d8b468a..f5e58c5 100644 --- a/imphnen-middleware/src/lib.rs +++ b/imphnen-middleware/src/lib.rs @@ -2,6 +2,6 @@ pub mod auth_middleware; pub mod cors_middleware; pub mod permissions_middleware; -pub use auth_middleware::*; -pub use cors_middleware::*; -pub use permissions_middleware::*; +pub use auth_middleware::auth_middleware; +pub use cors_middleware::cors_middleware; +pub use permissions_middleware::PermissionsMiddlewareLayer; diff --git a/imphnen-middleware/src/permissions_middleware/mod.rs b/imphnen-middleware/src/permissions_middleware/mod.rs index d21327f..d9172d6 100644 --- a/imphnen-middleware/src/permissions_middleware/mod.rs +++ b/imphnen-middleware/src/permissions_middleware/mod.rs @@ -9,6 +9,7 @@ use imphnen_utils::{common_response, extract_email, extract_email_async}; use std::task::{Context, Poll}; use tower::{Layer, Service}; +/// Middleware layer for enforcing user permissions on requests. #[derive(Clone)] pub struct PermissionsMiddlewareLayer { app_state: AppState, @@ -42,6 +43,7 @@ pub struct PermissionsMiddleware { permissions: Vec, } + impl Service> for PermissionsMiddleware where S: Service, Response = Response> + Clone + Send + 'static, diff --git a/imphnen-utils/src/lib.rs b/imphnen-utils/src/lib.rs index 723c0a2..7d24d17 100644 --- a/imphnen-utils/src/lib.rs +++ b/imphnen-utils/src/lib.rs @@ -1,39 +1,91 @@ -pub mod logger; pub mod bind_filter; +pub mod csrf_token; pub mod extract_email; pub mod generate_date; pub mod generate_otp; pub mod get_id; +pub mod logger; pub mod make_thing; +pub mod mock_test; pub mod query_builder; pub mod query_list; pub mod response_format; pub mod serde_helpers; pub mod validator; -pub mod csrf_token; -pub use logger::init_logger; -pub use bind_filter::*; +// Internal module re-exports +pub use bind_filter::bind_filter_value; +pub use csrf_token::{generate_csrf_token, generate_oauth_csrf_token, validate_csrf_token, validate_oauth_csrf_token}; pub use extract_email::{extract_email, extract_email_async, extract_email_token, extract_email_token_async}; -pub use generate_date::*; -pub use generate_otp::*; -pub use get_id::*; -pub use imphnen_entities::*; -pub use imphnen_libs::*; -pub use make_thing::*; +pub use generate_date::get_iso_date; +pub use generate_otp::OtpManager; +pub use get_id::{extract_id, get_id}; +pub use logger::init_logger; +pub use make_thing::{make_thing, make_thing_from_enum, make_thing_str}; pub use query_builder::{ - build_thing_condition, build_multi_thing_condition, - execute_safe_update_query, + build_thing_condition, execute_safe_count_query, - ListQueryBuilder, + execute_safe_update_query, DetailQueryBuilder, + ListQueryBuilder, }; pub use query_list::QueryListBuilder; -pub use response_format::*; +pub use response_format::{common_response, success_created_response, success_list_response, success_response}; pub use serde_helpers::{ - option_thing_or_string, serialize_option_thing, serialize_thing, - string_or_empty_string, thing_or_string, + deserialize_datetime, + option_thing_or_string, + serialize_datetime, + serialize_option_thing, + serialize_thing, + string_or_empty_string, + thing_or_string, +}; +pub use validator::validate_request; + +// External crate re-exports +pub use imphnen_libs::{ + AppState, + Claims, + CountResult, + EducationDto, + ENV, + Env, + Error, + ExperienceDto, + FileMetadata, + FileType, + MessageResponseDto, + MetaRequestDto, + MetaResponseDto, + MinioConfig, + MinioService, + PermissionsEnum, + PermissionsItemDto, + PermissionsQueryDto, + ResourceEnum, + ResponseListSuccessDto, + ResponseSuccessDto, + SurrealMemClient, + SurrealWsClient, + UploadRequest, + UploadResult, + UserLookupService, + UsersDetailQueryDto, + AuthRepositoryTrait, + axum_init, + create_minio_service_from_config, + decode_access_token, + decode_base64_file, + decode_refresh_token, + encode_access_token, + encode_refresh_token, + encode_reset_password_token, + extract_content_type_from_data_url, + generate_jwt, + hash_password, + send_email, + surrealdb_init_mem, + surrealdb_init_ws, + verify_password, }; -pub use validator::*; -pub use csrf_token::*; diff --git a/imphnen-utils/src/v1/mod.rs b/imphnen-utils/src/v1/mod.rs new file mode 100644 index 0000000..57941a8 --- /dev/null +++ b/imphnen-utils/src/v1/mod.rs @@ -0,0 +1 @@ +pub mod permissions; \ No newline at end of file diff --git a/imphnen-utils/src/v1/permissions/mod.rs b/imphnen-utils/src/v1/permissions/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/tests/src/dimentorin/mod.rs b/tests/src/dimentorin/mod.rs index 8ee3a1d..9dd2335 100644 --- a/tests/src/dimentorin/mod.rs +++ b/tests/src/dimentorin/mod.rs @@ -1 +1 @@ -pub mod mentor; +pub mod mentors; diff --git a/tests/src/iam/auth/auth_login_tests.rs b/tests/src/iam/auth/auth_login_tests.rs index 25c372d..d32f394 100644 --- a/tests/src/iam/auth/auth_login_tests.rs +++ b/tests/src/iam/auth/auth_login_tests.rs @@ -5,9 +5,11 @@ mod auth_login_tests { use crate::mock_test::setup_all_test_environment; use axum::http::StatusCode; use imphnen_iam::{ - v1::auth::{AuthLoginRequestDto, AuthService, AuthServiceTrait}, // Import AuthServiceTrait + v1::auth::AuthLoginRequestDto, AppState, UsersRepository, UsersSchema, }; + use imphnen_iam::v1::auth::auth_service::AuthService; + use imphnen_iam::v1::auth::AuthServiceTrait; use serde_json::Value; // Import the new setup function async fn setup_test_environment() -> AppState { @@ -343,7 +345,7 @@ mod auth_login_tests { assert_eq!(parts.status, StatusCode::OK); // Verify user was cached - let auth_repo = imphnen_iam::AuthRepository::new(&state); + let auth_repo = imphnen_iam::AuthRepository::new(state.surrealdb_mem.clone()); let cached_user = auth_repo.query_get_stored_user(email.clone()).await; assert!(cached_user.is_ok()); assert_eq!(cached_user.unwrap().email, email); diff --git a/tests/src/iam/auth/auth_repository_test.rs b/tests/src/iam/auth/auth_repository_test.rs index 0f318c3..f04b548 100644 --- a/tests/src/iam/auth/auth_repository_test.rs +++ b/tests/src/iam/auth/auth_repository_test.rs @@ -13,7 +13,8 @@ mod auth_repository_test { UsersSchema, }; use chrono::{Duration, Utc}; - use imphnen_iam::{AppState, RolesDetailQueryDto, UsersDetailQueryDto}; + use imphnen_iam::{AppState, UsersDetailQueryDto}; + use imphnen_entities::RolesDetailQueryDto; use surrealdb::Uuid; async fn create_mock_user(state: &AppState, email: &str) -> UsersSchema { @@ -53,8 +54,8 @@ mod auth_repository_test { #[tokio::test] async fn test_store_and_get_user() { - let app_state = setup_all_test_environment().await; // Use the new setup function - let repo = AuthRepository::new(&app_state); + let app_state = setup_all_test_environment().await; // Use the new setup function + let repo = AuthRepository::new(app_state.surrealdb_mem.clone()); let email = generate_unique_email("forgot"); let mut user = create_mock_user(&app_state, &email).await; user.role = get_role_id("user", &app_state).await; @@ -81,8 +82,8 @@ mod auth_repository_test { #[tokio::test] async fn test_delete_stored_user() { - let state = setup_all_test_environment().await; // Use the new setup function - let auth_repo = AuthRepository::new(&state); + let state = setup_all_test_environment().await; // Use the new setup function + let auth_repo = AuthRepository::new(state.surrealdb_mem.clone()); let email = "delete_me@example.com".to_string(); let mock_user = UsersDetailQueryDto { id: make_thing(&ResourceEnum::UsersCache.to_string(), &email), @@ -144,8 +145,8 @@ mod auth_repository_test { #[tokio::test] async fn test_store_and_get_otp() { - let app_state = setup_all_test_environment().await; // Use the new setup function - let repo = AuthRepository::new(&app_state); + let app_state = setup_all_test_environment().await; // Use the new setup function + let repo = AuthRepository::new(app_state.surrealdb_mem.clone()); let email = "otp_user@example.com".to_string(); let otp = 123456; let stored = repo.query_store_otp(email.clone(), otp).await; @@ -157,8 +158,8 @@ mod auth_repository_test { #[tokio::test] async fn test_delete_stored_otp() { - let app_state = setup_all_test_environment().await; // Use the new setup function - let repo = AuthRepository::new(&app_state); + let app_state = setup_all_test_environment().await; // Use the new setup function + let repo = AuthRepository::new(app_state.surrealdb_mem.clone()); let email = "otp_del@example.com".to_string(); let otp = 654321; let store_res = repo.query_store_otp(email.clone(), otp).await; @@ -178,18 +179,17 @@ mod auth_repository_test { #[tokio::test] async fn test_expired_otp() { - let app_state = setup_all_test_environment().await; // Use the new setup function - let repo = AuthRepository::new(&app_state); + let app_state = setup_all_test_environment().await; // Use the new setup function + let repo = AuthRepository::new(app_state.surrealdb_mem.clone()); let email = "expired_otp@example.com".to_string(); let otp = 789012; let table = ResourceEnum::OtpCache.to_string(); let expires_at = Utc::now() - Duration::seconds(1); let created: Result, surrealdb::Error> = repo - .state - .surrealdb_mem - .create((table.clone(), email.as_str())) - .content(AuthOtpSchema { otp, expires_at }) - .await; + .db + .create((table.clone(), email.as_str())) + .content(AuthOtpSchema { otp, expires_at }) + .await; assert!( created.is_ok(), "Failed to create expired OTP: {:?}", @@ -210,8 +210,8 @@ mod auth_repository_test { #[tokio::test] async fn test_get_non_existent_stored_user_should_fail() { - let app_state = setup_all_test_environment().await; // Use the new setup function - let repo = AuthRepository::new(&app_state); + let app_state = setup_all_test_environment().await; // Use the new setup function + let repo = AuthRepository::new(app_state.surrealdb_mem.clone()); let result = repo .query_get_stored_user("not_found@example.com".into()) .await; @@ -226,8 +226,8 @@ mod auth_repository_test { #[tokio::test] async fn test_delete_non_existent_user_should_fail() { - let app_state = setup_all_test_environment().await; // Use the new setup function - let repo = AuthRepository::new(&app_state); + let app_state = setup_all_test_environment().await; // Use the new setup function + let repo = AuthRepository::new(app_state.surrealdb_mem.clone()); let result = repo .query_delete_stored_user("ghost@example.com".into()) .await; @@ -242,8 +242,8 @@ mod auth_repository_test { #[tokio::test] async fn test_store_and_get_valid_otp() { - let app_state = setup_all_test_environment().await; // Use the new setup function - let repo = AuthRepository::new(&app_state); + let app_state = setup_all_test_environment().await; // Use the new setup function + let repo = AuthRepository::new(app_state.surrealdb_mem.clone()); let email = "valid_otp@example.com"; let otp = 654321; let store_result = repo.query_store_otp(email.into(), otp).await; diff --git a/tests/src/iam/auth/google/mod.rs b/tests/src/iam/auth/google/mod.rs index e69de29..fabd833 100644 --- a/tests/src/iam/auth/google/mod.rs +++ b/tests/src/iam/auth/google/mod.rs @@ -0,0 +1,2 @@ +#[cfg(test)] +pub mod google_oauth_flow_test; \ No newline at end of file diff --git a/tests/src/iam/roles/roles_repository_test.rs b/tests/src/iam/roles/roles_repository_test.rs index 56f20c5..53a7470 100644 --- a/tests/src/iam/roles/roles_repository_test.rs +++ b/tests/src/iam/roles/roles_repository_test.rs @@ -8,8 +8,8 @@ mod tests { #[tokio::test] async fn test_query_create_role() { - let app_state = crate::get_app_state().await; - let repo = imphnen_iam::RolesRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = imphnen_iam::RolesRepository::new(&app_state); // Test data let role_name = "test_role_repo_create".to_string(); @@ -35,8 +35,8 @@ mod tests { #[tokio::test] async fn test_query_role_by_name() { - let app_state = crate::get_app_state().await; - let repo = imphnen_iam::RolesRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = imphnen_iam::RolesRepository::new(&app_state); // Test data let role_name = "test_role_repo_by_name".to_string(); @@ -66,8 +66,8 @@ mod tests { #[tokio::test] async fn test_query_role_list() { - let app_state = crate::get_app_state().await; - let repo = imphnen_iam::RolesRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = imphnen_iam::RolesRepository::new(&app_state); // Create test roles let role_names = vec![ @@ -108,8 +108,8 @@ mod tests { #[tokio::test] async fn test_query_update_role() { - let app_state = crate::get_app_state().await; - let repo = imphnen_iam::RolesRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = imphnen_iam::RolesRepository::new(&app_state); // Test data let original_name = "test_role_update_original".to_string(); @@ -149,8 +149,8 @@ mod tests { #[tokio::test] async fn test_query_delete_role() { - let app_state = crate::get_app_state().await; - let repo = imphnen_iam::RolesRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = imphnen_iam::RolesRepository::new(&app_state); // Test data let role_name = "test_role_delete".to_string(); diff --git a/tests/src/iam/teams/mod.rs b/tests/src/iam/teams/mod.rs index a94c2bc..97bd6df 100644 --- a/tests/src/iam/teams/mod.rs +++ b/tests/src/iam/teams/mod.rs @@ -1,2 +1,4 @@ +#[cfg(test)] pub mod teams_repository_test; +#[cfg(test)] pub mod teams_service_test; \ No newline at end of file diff --git a/tests/src/iam/teams/teams_repository_test.rs b/tests/src/iam/teams/teams_repository_test.rs index 1a62746..668bae3 100644 --- a/tests/src/iam/teams/teams_repository_test.rs +++ b/tests/src/iam/teams/teams_repository_test.rs @@ -49,8 +49,8 @@ mod tests { #[tokio::test] async fn test_create_team() { - let app_state = crate::get_app_state().await; - let repo = TeamsRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = TeamsRepository::new(&app_state); let team_id = Uuid::new_v4().to_string(); let leader_id = Uuid::new_v4().to_string(); @@ -81,8 +81,8 @@ mod tests { #[tokio::test] async fn test_team_list() { - let app_state = crate::get_app_state().await; - let repo = TeamsRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = TeamsRepository::new(&app_state); let team_id = Uuid::new_v4().to_string(); let leader_id = Uuid::new_v4().to_string(); @@ -116,8 +116,8 @@ mod tests { #[tokio::test] async fn test_update_team() { - let app_state = crate::get_app_state().await; - let repo = TeamsRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = TeamsRepository::new(&app_state); let team_id = Uuid::new_v4().to_string(); let leader_id = Uuid::new_v4().to_string(); @@ -148,8 +148,8 @@ mod tests { #[tokio::test] async fn test_delete_team() { - let app_state = crate::get_app_state().await; - let repo = TeamsRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = TeamsRepository::new(&app_state); let team_id = Uuid::new_v4().to_string(); let leader_id = Uuid::new_v4().to_string(); @@ -175,8 +175,8 @@ mod tests { #[tokio::test] async fn test_add_team_member() { - let app_state = crate::get_app_state().await; - let repo = TeamsRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = TeamsRepository::new(&app_state); let team_id = Uuid::new_v4().to_string(); let leader_id = Uuid::new_v4().to_string(); @@ -250,8 +250,8 @@ mod tests { #[tokio::test] async fn test_team_member_check() { - let app_state = crate::get_app_state().await; - let repo = TeamsRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = TeamsRepository::new(&app_state); let team_id = Uuid::new_v4().to_string(); let leader_id = Uuid::new_v4().to_string(); @@ -296,8 +296,8 @@ mod tests { #[tokio::test] async fn test_create_invitation() { - let app_state = crate::get_app_state().await; - let repo = TeamsRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = TeamsRepository::new(&app_state); let team_id = Uuid::new_v4().to_string(); let inviter_id = Uuid::new_v4().to_string(); @@ -332,8 +332,8 @@ mod tests { #[tokio::test] async fn test_search_teams() { - let app_state = crate::get_app_state().await; - let repo = TeamsRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = TeamsRepository::new(&app_state); let team_id = Uuid::new_v4().to_string(); let leader_id = Uuid::new_v4().to_string(); @@ -366,8 +366,8 @@ mod tests { #[tokio::test] async fn test_remove_team_member() { - let app_state = crate::get_app_state().await; - let repo = TeamsRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = TeamsRepository::new(&app_state); let team_id = Uuid::new_v4().to_string(); let leader_id = Uuid::new_v4().to_string(); diff --git a/tests/src/iam/teams/teams_service_test.rs b/tests/src/iam/teams/teams_service_test.rs index cf959cc..2fd04ba 100644 --- a/tests/src/iam/teams/teams_service_test.rs +++ b/tests/src/iam/teams/teams_service_test.rs @@ -9,9 +9,9 @@ mod tests { #[tokio::test] async fn test_create_team_service() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = TeamsRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = TeamsRepository::new(&app_state); let email = generate_unique_email("team_creator"); let role_id = get_role_id("mentee", &app_state).await; @@ -54,9 +54,9 @@ mod tests { #[tokio::test] async fn test_get_team_service() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = TeamsRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = TeamsRepository::new(&app_state); let email = generate_unique_email("team_getter"); let role_id = get_role_id("mentee", &app_state).await; @@ -101,9 +101,9 @@ mod tests { #[tokio::test] async fn test_update_team_service() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = TeamsRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = TeamsRepository::new(&app_state); let email = generate_unique_email("team_updater"); let role_id = get_role_id("mentee", &app_state).await; @@ -171,9 +171,9 @@ mod tests { #[tokio::test] async fn test_invite_team_member_service() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = TeamsRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = TeamsRepository::new(&app_state); let leader_email = generate_unique_email("team_leader"); let role_id = get_role_id("mentee", &app_state).await; @@ -228,9 +228,9 @@ mod tests { #[tokio::test] async fn test_leave_team_service() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = TeamsRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = TeamsRepository::new(&app_state); let leader_email = generate_unique_email("leave_leader"); let member_email = generate_unique_email("leave_member"); @@ -309,9 +309,9 @@ mod tests { #[tokio::test] async fn test_search_teams_service() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = TeamsRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = TeamsRepository::new(&app_state); let email = generate_unique_email("search_creator"); let role_id = get_role_id("mentee", &app_state).await; @@ -374,9 +374,9 @@ mod tests { #[tokio::test] async fn test_delete_team_service() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = TeamsRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = TeamsRepository::new(&app_state); let email = generate_unique_email("delete_creator"); let role_id = get_role_id("mentee", &app_state).await; @@ -428,9 +428,9 @@ mod tests { #[tokio::test] async fn test_unauthorized_operations() { - let app_state = crate::get_app_state().await; - let users_repo = UsersRepository::new(&app_state); - let repo = TeamsRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let users_repo = UsersRepository::new(&app_state); + let repo = TeamsRepository::new(&app_state); let leader_email = generate_unique_email("auth_leader"); let non_leader_email = generate_unique_email("auth_non_leader"); diff --git a/tests/src/iam/users/users_repository_test.rs b/tests/src/iam/users/users_repository_test.rs index 531e848..5acc4b2 100644 --- a/tests/src/iam/users/users_repository_test.rs +++ b/tests/src/iam/users/users_repository_test.rs @@ -7,8 +7,8 @@ mod tests { #[tokio::test] async fn test_query_create_user() { - let app_state = crate::get_app_state().await; - let repo = UsersRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = UsersRepository::new(&app_state); let role_id = get_role_id("user", &app_state).await; // Test data @@ -41,8 +41,8 @@ mod tests { #[tokio::test] async fn test_query_user_by_email() { - let app_state = crate::get_app_state().await; - let repo = UsersRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = UsersRepository::new(&app_state); let role_id = get_role_id("user", &app_state).await; // Test data @@ -75,8 +75,8 @@ mod tests { #[tokio::test] async fn test_query_user_by_email_not_found() { - let app_state = crate::get_app_state().await; - let repo = UsersRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = UsersRepository::new(&app_state); // Try to get non-existent user let result = repo.query_user_by_email("nonexistent@example.com".to_string()).await; @@ -85,8 +85,8 @@ mod tests { #[tokio::test] async fn test_query_update_user() { - let app_state = crate::get_app_state().await; - let repo = UsersRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = UsersRepository::new(&app_state); let role_id = get_role_id("user", &app_state).await; // Test data @@ -133,8 +133,8 @@ mod tests { #[tokio::test] async fn test_query_delete_user() { - let app_state = crate::get_app_state().await; - let repo = UsersRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = UsersRepository::new(&app_state); let role_id = get_role_id("user", &app_state).await; // Test data @@ -168,8 +168,8 @@ mod tests { #[tokio::test] async fn test_query_user_list() { - let app_state = crate::get_app_state().await; - let repo = UsersRepository::new(&app_state); + let app_state = crate::get_app_state().await; + let repo = UsersRepository::new(&app_state); let role_id = get_role_id("user", &app_state).await; // Create test users diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 2e3fe5a..0bc613c 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -1,7 +1,8 @@ use ::surrealdb::Uuid; use ::surrealdb::sql; -pub use imphnen_entities::*; -pub use imphnen_iam::*; +pub use imphnen_entities::MetaRequestDto; +pub use imphnen_iam::{ResourceEnum, RolesRepository, UsersRepository, AuthOtpSchema, AuthRepository, RolesDetailQueryDto, UsersDetailQueryDto, RolesRequestCreateDto, RolesRequestUpdateDto, RolesDetailItemDto, TeamsRepository, TeamsSchema, TeamMembersSchema, TeamInvitationsSchema, UsersSchema}; +use imphnen_libs::AppState; pub fn create_test_mentor( email: &str, @@ -69,7 +70,7 @@ pub fn generate_unique_email(prefix: &str) -> String { format!("{}_{}@example.com", prefix, Uuid::new_v4()) } -pub async fn get_role_id(role_name: &str, state: &crate::AppState) -> sql::Thing { +pub async fn get_role_id(role_name: &str, state: &AppState) -> sql::Thing { let repo = RolesRepository::new(state); if let Ok(existing) = repo.query_role_by_name(role_name.into()).await { return make_thing(&ResourceEnum::Roles.to_string(), &existing.id); @@ -87,7 +88,7 @@ pub async fn get_role_id(role_name: &str, state: &crate::AppState) -> sql::Thing make_thing(&ResourceEnum::Roles.to_string(), &role.id) } -pub async fn get_app_state() -> crate::AppState { +pub async fn get_app_state() -> AppState { create_mock_app_state().await }