feat(presentation): NatsEventPublisher with no-op fallback when NATS_URL unset
This commit is contained in:
@@ -14,6 +14,8 @@ api-types = { workspace = true }
|
|||||||
postgres = { workspace = true }
|
postgres = { workspace = true }
|
||||||
postgres-search = { workspace = true }
|
postgres-search = { workspace = true }
|
||||||
auth = { workspace = true }
|
auth = { workspace = true }
|
||||||
|
nats = { workspace = true }
|
||||||
|
async-nats = { workspace = true }
|
||||||
axum = { workspace = true }
|
axum = { workspace = true }
|
||||||
sqlx = { workspace = true }
|
sqlx = { workspace = true }
|
||||||
tower-http = { workspace = true }
|
tower-http = { workspace = true }
|
||||||
|
|||||||
@@ -5,23 +5,36 @@ pub mod routes;
|
|||||||
pub mod state;
|
pub mod state;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use sqlx::PgPool;
|
|
||||||
use state::AppState;
|
|
||||||
use postgres_search::PgSearchRepository;
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
use sqlx::PgPool;
|
||||||
use domain::{errors::DomainError, events::DomainEvent, ports::EventPublisher};
|
use domain::{errors::DomainError, events::DomainEvent, ports::EventPublisher};
|
||||||
|
use postgres_search::PgSearchRepository;
|
||||||
|
use state::AppState;
|
||||||
|
|
||||||
struct NoOpEventPublisher;
|
struct NoOpEventPublisher;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl EventPublisher for NoOpEventPublisher {
|
impl EventPublisher for NoOpEventPublisher {
|
||||||
async fn publish(&self, _e: &DomainEvent) -> Result<(), DomainError> {
|
async fn publish(&self, _e: &DomainEvent) -> Result<(), DomainError> { Ok(()) }
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_state(pool: PgPool, jwt_secret: String) -> AppState {
|
pub async fn build_state(pool: PgPool, jwt_secret: String) -> AppState {
|
||||||
|
let event_publisher: Arc<dyn EventPublisher> = match std::env::var("NATS_URL") {
|
||||||
|
Ok(url) => match async_nats::connect(&url).await {
|
||||||
|
Ok(client) => {
|
||||||
|
tracing::info!("Connected to NATS at {url}");
|
||||||
|
Arc::new(nats::NatsEventPublisher::new(client))
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
tracing::warn!("Failed to connect to NATS at {url}: {e} — using no-op publisher");
|
||||||
|
Arc::new(NoOpEventPublisher)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
tracing::info!("NATS_URL not set — using no-op event publisher");
|
||||||
|
Arc::new(NoOpEventPublisher)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
AppState {
|
AppState {
|
||||||
users: Arc::new(postgres::user::PgUserRepository::new(pool.clone())),
|
users: Arc::new(postgres::user::PgUserRepository::new(pool.clone())),
|
||||||
thoughts: Arc::new(postgres::thought::PgThoughtRepository::new(pool.clone())),
|
thoughts: Arc::new(postgres::thought::PgThoughtRepository::new(pool.clone())),
|
||||||
@@ -38,6 +51,6 @@ pub fn build_state(pool: PgPool, jwt_secret: String) -> AppState {
|
|||||||
search: Arc::new(PgSearchRepository::new(pool.clone())),
|
search: Arc::new(PgSearchRepository::new(pool.clone())),
|
||||||
auth: Arc::new(auth::JwtAuthService::new(jwt_secret, 86400 * 30)),
|
auth: Arc::new(auth::JwtAuthService::new(jwt_secret, 86400 * 30)),
|
||||||
hasher: Arc::new(auth::Argon2PasswordHasher),
|
hasher: Arc::new(auth::Argon2PasswordHasher),
|
||||||
events: Arc::new(NoOpEventPublisher),
|
events: event_publisher,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ async fn main() {
|
|||||||
let pool = PgPool::connect(&database_url).await.expect("DB connect failed");
|
let pool = PgPool::connect(&database_url).await.expect("DB connect failed");
|
||||||
sqlx::migrate!("../adapters/postgres/migrations").run(&pool).await.expect("Migrations failed");
|
sqlx::migrate!("../adapters/postgres/migrations").run(&pool).await.expect("Migrations failed");
|
||||||
|
|
||||||
let state = presentation::build_state(pool, jwt_secret);
|
let state = presentation::build_state(pool, jwt_secret).await;
|
||||||
let app = presentation::routes::router()
|
let app = presentation::routes::router()
|
||||||
.with_state(state)
|
.with_state(state)
|
||||||
.layer(CorsLayer::permissive());
|
.layer(CorsLayer::permissive());
|
||||||
|
|||||||
Reference in New Issue
Block a user