From 60ad3c2f12b203cbadc97248c5d04fa3a552cdbf Mon Sep 17 00:00:00 2001 From: Guillaume Deconinck Date: Thu, 11 Jun 2026 13:58:09 +0900 Subject: [PATCH 1/4] fix: improve migrate and pg pool setup --- bins/nittei/src/bin/migrate.rs | 2 ++ crates/infra/src/lib.rs | 9 ++++----- crates/infra/src/repos/mod.rs | 35 +++++++++++++++++++--------------- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/bins/nittei/src/bin/migrate.rs b/bins/nittei/src/bin/migrate.rs index 63e26de0..3bbacd42 100644 --- a/bins/nittei/src/bin/migrate.rs +++ b/bins/nittei/src/bin/migrate.rs @@ -7,6 +7,8 @@ async fn main() -> anyhow::Result<()> { // Initialize the subscriber for logging & tracing init_subscriber()?; + tracing::info!("Running migrations"); + run_migration().await.inspect_err(|e| { tracing::error!(error = ?e, "Failed to run migrations"); })?; diff --git a/crates/infra/src/lib.rs b/crates/infra/src/lib.rs index 8c82464c..be70d832 100644 --- a/crates/infra/src/lib.rs +++ b/crates/infra/src/lib.rs @@ -17,10 +17,11 @@ pub use repos::{ SearchEventsParams, }; pub use services::*; -use sqlx::postgres::PgPoolOptions; pub use system::ISys; use system::RealSys; +use crate::repos::create_postgres_pool; + /// The context for the application /// Contains the repositories, configuration, and system /// @@ -61,10 +62,8 @@ pub async fn setup_context() -> anyhow::Result { /// This is not run by the application itself, but is provided as a utility /// Usage is in bins/nittei/src/bin/migrate.rs pub async fn run_migration() -> anyhow::Result<()> { - let pool = PgPoolOptions::new() - .max_connections(5) - .connect(nittei_utils::config::APP_CONFIG.pg.database_url.as_str()) - .await?; + let pool = + create_postgres_pool(nittei_utils::config::APP_CONFIG.pg.database_url.as_str()).await?; sqlx::migrate!().run(&pool).await.map_err(|e| e.into()) } diff --git a/crates/infra/src/repos/mod.rs b/crates/infra/src/repos/mod.rs index 082b42de..5c7d2403 100644 --- a/crates/infra/src/repos/mod.rs +++ b/crates/infra/src/repos/mod.rs @@ -97,24 +97,29 @@ pub struct Repos { pub user_integrations: Arc, } +pub async fn create_postgres_pool(connection_string: &str) -> anyhow::Result> { + let pool = PgPoolOptions::new() + .min_connections(nittei_utils::config::APP_CONFIG.pg.min_connections) + .max_connections(nittei_utils::config::APP_CONFIG.pg.max_connections) + .acquire_timeout(Duration::from_secs(3)) // Max time to wait for a connection + .connect(connection_string) + .await + .context(format!( + "Failed to connect to PG url '{}'", + remove_password_from_url(connection_string)? + ))?; + + info!("[repos] Postgres connection created"); + + Ok(pool) +} + impl Repos { pub async fn create_postgres(connection_string: &str) -> anyhow::Result { // Register metrics register_metrics()?; - info!("[repos] Creating postgres connection"); - let pool = PgPoolOptions::new() - .min_connections(nittei_utils::config::APP_CONFIG.pg.min_connections) - .max_connections(nittei_utils::config::APP_CONFIG.pg.max_connections) - .acquire_timeout(Duration::from_secs(3)) // Max time to wait for a connection - .connect(connection_string) - .await - .context(format!( - "Failed to connect to PG url '{}'", - remove_password_from_url(connection_string)? - ))?; - - info!("[repos] Postgres connection created"); + let pool = create_postgres_pool(connection_string).await?; // Create monitored pool let monitored_pool = MonitoredPgPool::new(pool.clone()); @@ -185,7 +190,7 @@ fn remove_password_from_url(connection_string: &str) -> anyhow::Result { // If the connection string is not a valid URL, return the connection string as is Err(_) => return Ok(connection_string.to_string()), }; - #[allow(clippy::unwrap_used)] - url.set_password(Some("*********")).unwrap(); + // Ignore result as it's only best effort + let _ = url.set_password(Some("*********")); Ok(url.to_string()) } From 4a3f305617faf406ba59110aeb5d7fd80ffefe04 Mon Sep 17 00:00:00 2001 From: Guillaume Deconinck Date: Thu, 11 Jun 2026 14:22:14 +0900 Subject: [PATCH 2/4] fix: improve password removal utility --- crates/infra/src/repos/mod.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/infra/src/repos/mod.rs b/crates/infra/src/repos/mod.rs index 5c7d2403..f7f14277 100644 --- a/crates/infra/src/repos/mod.rs +++ b/crates/infra/src/repos/mod.rs @@ -184,13 +184,18 @@ impl Repos { } } +/// Remove the password from the connection string +/// This is a best effort function, so if it fails, we return the connection string as is fn remove_password_from_url(connection_string: &str) -> anyhow::Result { let mut url = match url::Url::parse(connection_string) { Ok(url) => url, // If the connection string is not a valid URL, return the connection string as is Err(_) => return Ok(connection_string.to_string()), }; - // Ignore result as it's only best effort - let _ = url.set_password(Some("*********")); + + // If we have a password, try to remove it + if url.password().is_some() && url.set_password(Some("*********")).is_err() { + return Ok(connection_string.to_string()); + } Ok(url.to_string()) } From b3f16a32b2bbb5e73b310a762d1284ef28bb0238 Mon Sep 17 00:00:00 2001 From: Guillaume Deconinck Date: Thu, 11 Jun 2026 14:25:01 +0900 Subject: [PATCH 3/4] fix: avoid leaking password --- crates/infra/src/repos/mod.rs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/crates/infra/src/repos/mod.rs b/crates/infra/src/repos/mod.rs index f7f14277..9e385770 100644 --- a/crates/infra/src/repos/mod.rs +++ b/crates/infra/src/repos/mod.rs @@ -106,7 +106,7 @@ pub async fn create_postgres_pool(connection_string: &str) -> anyhow::Result anyhow::Result { - let mut url = match url::Url::parse(connection_string) { - Ok(url) => url, - // If the connection string is not a valid URL, return the connection string as is - Err(_) => return Ok(connection_string.to_string()), +/// Redact the password from the connection string +fn redact_password_from_url(connection_string: &str) -> String { + let Ok(mut url) = url::Url::parse(connection_string) else { + return "".to_string(); }; - - // If we have a password, try to remove it if url.password().is_some() && url.set_password(Some("*********")).is_err() { - return Ok(connection_string.to_string()); + return "".to_string(); } - Ok(url.to_string()) + url.to_string() } From 38f177a331898f1d8c747d2e47e5837ba22e431c Mon Sep 17 00:00:00 2001 From: Guillaume Deconinck Date: Thu, 11 Jun 2026 14:28:10 +0900 Subject: [PATCH 4/4] fix: re-add log --- crates/infra/src/repos/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/infra/src/repos/mod.rs b/crates/infra/src/repos/mod.rs index 9e385770..568953dd 100644 --- a/crates/infra/src/repos/mod.rs +++ b/crates/infra/src/repos/mod.rs @@ -98,6 +98,8 @@ pub struct Repos { } pub async fn create_postgres_pool(connection_string: &str) -> anyhow::Result> { + info!("[repos] Creating postgres connection"); + let pool = PgPoolOptions::new() .min_connections(nittei_utils::config::APP_CONFIG.pg.min_connections) .max_connections(nittei_utils::config::APP_CONFIG.pg.max_connections)