#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
#![forbid(unsafe_code)]
pub mod auth;
pub mod cache;
pub mod database;
pub mod domain;
pub mod encryption;
pub mod error;
pub mod middleware;
pub mod repository;
pub use auth::{Claims, JwtService};
pub use cache::{MemoryTenantCache, TenantCache};
pub use database::TenantDatabase;
pub use domain::{Tenant, TenantContext};
pub use encryption::{EncryptedData, EncryptionService};
pub use error::{CorteqError, Result};
pub use middleware::{TenantContextMiddleware, TenantExtractor};
pub use repository::TenantRepository;
use sqlx::PgPool;
use std::sync::Arc;
#[derive(Clone, Debug)]
pub struct CorteqApp {
pub db_pool: PgPool,
pub jwt_service: JwtService,
pub tenant_cache: Arc<dyn TenantCache>,
pub tenant_repository: TenantRepository,
}
impl CorteqApp {
pub fn builder() -> CorteqAppBuilder {
CorteqAppBuilder::default()
}
}
#[derive(Default, Debug)]
pub struct CorteqAppBuilder {
database_url: Option<String>,
jwt_secret: Option<String>,
cache: Option<Arc<dyn TenantCache>>,
}
impl CorteqAppBuilder {
pub fn with_database(mut self, url: &str) -> Self {
self.database_url = Some(url.to_string());
self
}
pub fn with_jwt_secret(mut self, secret: &str) -> Self {
self.jwt_secret = Some(secret.to_string());
self
}
pub fn with_cache(mut self, cache: Arc<dyn TenantCache>) -> Self {
self.cache = Some(cache);
self
}
pub async fn build(self) -> Result<CorteqApp> {
let database_url = self
.database_url
.ok_or_else(|| CorteqError::ConfigError("Database URL is required".to_string()))?;
let jwt_secret = self
.jwt_secret
.ok_or_else(|| CorteqError::ConfigError("JWT secret is required".to_string()))?;
tracing::info!("Connecting to database: {}", database_url);
let db_pool = PgPool::connect(&database_url)
.await
.map_err(CorteqError::DatabaseError)?;
tracing::info!("Running database migrations...");
sqlx::migrate!("./migrations")
.run(&db_pool)
.await
.map_err(|e| CorteqError::ConfigError(format!("Migration failed: {e}")))?;
let jwt_service = JwtService::new(jwt_secret.as_bytes());
let tenant_cache = self.cache.unwrap_or_else(|| {
tracing::info!("Using default in-memory tenant cache");
Arc::new(MemoryTenantCache::default())
});
let tenant_repository = TenantRepository::new(db_pool.clone());
tracing::info!("✓ Corteq application initialized successfully");
Ok(CorteqApp {
db_pool,
jwt_service,
tenant_cache,
tenant_repository,
})
}
}