diff --git a/Cargo.lock b/Cargo.lock index c993b12..99532ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -243,18 +243,11 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be" dependencies = [ - "async-trait", - "convert_case", - "json5", "lazy_static", "nom", "pathdiff", - "ron", - "rust-ini", "serde", - "serde_json", "toml", - "yaml-rust", ] [[package]] @@ -263,35 +256,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "const-random" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom", - "once_cell", - "tiny-keccak", -] - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "core-foundation" version = "0.9.4" @@ -347,12 +311,6 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-common" version = "0.1.6" @@ -395,15 +353,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "dlv-list" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" -dependencies = [ - "const-random", -] - [[package]] name = "dotenvy" version = "0.15.7" @@ -642,12 +591,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" - [[package]] name = "hashbrown" version = "0.14.3" @@ -664,7 +607,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" dependencies = [ - "hashbrown 0.14.3", + "hashbrown", ] [[package]] @@ -858,7 +801,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown", ] [[package]] @@ -891,17 +834,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "json5" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" -dependencies = [ - "pest", - "pest_derive", - "serde", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -934,12 +866,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -1185,16 +1111,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "ordered-multimap" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e" -dependencies = [ - "dlv-list", - "hashbrown 0.13.2", -] - [[package]] name = "overload" version = "0.1.1" @@ -1251,51 +1167,6 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "pest" -version = "2.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f8023d0fb78c8e03784ea1c7f3fa36e68a723138990b8d5a47d916b651e7a8" -dependencies = [ - "memchr", - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0d24f72393fd16ab6ac5738bc33cdb6a9aa73f8b902e8fe29cf4e67d7dd1026" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc17e2a6c7d0a492f0158d7a4bd66cc17280308bbaff78d5bef566dca35ab80" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.55", -] - -[[package]] -name = "pest_meta" -version = "2.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934cd7631c050f4674352a6e835d5f6711ffbfb9345c2fc0107155ac495ae293" -dependencies = [ - "once_cell", - "pest", - "sha2", -] - [[package]] name = "pin-project" version = "1.1.5" @@ -1525,18 +1396,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "ron" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" -dependencies = [ - "base64", - "bitflags 2.5.0", - "serde", - "serde_derive", -] - [[package]] name = "rsa" version = "0.9.6" @@ -1557,16 +1416,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rust-ini" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091" -dependencies = [ - "cfg-if", - "ordered-multimap", -] - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1695,6 +1544,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-aux" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d2e8bfba469d06512e11e3311d4d051a4a387a5b42d010404fecf3200321c95" +dependencies = [ + "chrono", + "serde", + "serde_json", +] + [[package]] name = "serde_derive" version = "1.0.197" @@ -2203,15 +2063,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -2474,12 +2325,6 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" -[[package]] -name = "ucd-trie" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" - [[package]] name = "unicode-bidi" version = "0.3.15" @@ -2859,15 +2704,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "zero2prod" version = "0.1.0" @@ -2879,6 +2715,7 @@ dependencies = [ "reqwest", "secrecy", "serde", + "serde-aux", "sqlx", "tokio", "tower-http", diff --git a/Cargo.toml b/Cargo.toml index 1ccefab..6cf111b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,9 +16,9 @@ name = "zero2prod" [dependencies] axum = "0.7" chrono = { version = "0.4", features = ["serde", "clock"] } -config = "0.14" +config = { version = "0.14", features = ["toml"], default-features = false } serde = { version = "1", features = ["derive"] } -sqlx = { version = "0.7", features = [ +sqlx = { version = "0.7", default-features = false, features = [ "runtime-tokio", "tls-rustls", "macros", @@ -35,6 +35,7 @@ tower-http = { version = "0.5", features = ["trace"] } tracing-bunyan-formatter = "0.3" tracing-log = "0.2" secrecy = { version = "0.8", features = ["serde"] } +serde-aux = "4" [dev-dependencies] reqwest = "0.12" diff --git a/config/local.toml b/config/local.toml index 8ef25b1..1be5491 100644 --- a/config/local.toml +++ b/config/local.toml @@ -1,2 +1,5 @@ [application] host = "127.0.0.1" + +[database] +require_ssl = false diff --git a/config/production.toml b/config/production.toml index d0de4b1..9775701 100644 --- a/config/production.toml +++ b/config/production.toml @@ -1,2 +1,5 @@ [application] host = "0.0.0.0" + +[database] +require_ssl = true diff --git a/spec.yml b/spec.yml index b494071..f74b7c3 100644 --- a/spec.yml +++ b/spec.yml @@ -2,6 +2,22 @@ name: zero2prod region: ams3 services: - name: zero2prod + envs: + - key: APP_DATABASE__USERNAME + scope: RUN_TIME + value: ${newsletter.USERNAME} + - key: APP_DATABASE__PASSWORD + scope: RUN_TIME + value: ${newsletter.PASSWORD} + - key: APP_DATABASE__HOST + scope: RUN_TIME + value: ${newsletter.HOSTNAME} + - key: APP_DATABASE__PORT + scope: RUN_TIME + value: ${newsletter.PORT} + - key: APP_DATABASE__DATABASE_NAME + scope: RUN_TIME + value: ${newsletter.DATABASE} dockerfile_path: Dockerfile source_dir: . github: diff --git a/src/config.rs b/src/config.rs index 30526ff..e12c04f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,6 +2,11 @@ use std::fmt::Display; use secrecy::{ExposeSecret, Secret}; use serde::Deserialize; +use serde_aux::field_attributes::deserialize_number_from_string; +use sqlx::{ + postgres::{PgConnectOptions, PgSslMode}, + ConnectOptions, +}; #[derive(Debug, Deserialize)] pub struct Settings { @@ -13,36 +18,24 @@ pub struct Settings { pub struct DatabaseSettings { pub username: String, pub password: Secret, + #[serde(deserialize_with = "deserialize_number_from_string")] pub port: u16, pub host: String, pub database_name: String, + pub require_ssl: bool, } + #[derive(Debug, Deserialize)] pub struct ApplicationSettings { + #[serde(deserialize_with = "deserialize_number_from_string")] pub port: u16, pub host: String, } -impl DatabaseSettings { - pub fn to_string_no_db(&self) -> Secret { - Secret::new(format!( - "postgres://{}:{}@{}:{}", - self.username, - self.password.expose_secret(), - self.host, - self.port - )) - } - pub fn to_string(&self) -> Secret { - Secret::new(format!( - "postgres://{}:{}@{}:{}/{}", - self.username, - self.password.expose_secret(), - self.host, - self.port, - self.database_name - )) - } +#[derive(Debug)] +pub enum Environment { + Local, + Production, } pub fn get_config() -> Result { @@ -58,14 +51,36 @@ pub fn get_config() -> Result { let settings = config::Config::builder() .add_source(config::File::from(config_directory.join("base.toml"))) .add_source(config::File::from(config_directory.join(env_filename))) + .add_source( + config::Environment::with_prefix("APP") + .prefix_separator("_") + .separator("__"), + ) .build()?; settings.try_deserialize::() } -#[derive(Debug)] -pub enum Environment { - Local, - Production, +impl DatabaseSettings { + pub fn without_db(&self) -> PgConnectOptions { + let ssl_mode = if self.require_ssl { + PgSslMode::Require + } else { + PgSslMode::Prefer + }; + + PgConnectOptions::new() + .host(&self.host) + .username(&self.username) + .password(self.password.expose_secret()) + .port(self.port) + .ssl_mode(ssl_mode) + } + + pub fn with_db(&self) -> PgConnectOptions { + self.without_db() + .database(&self.database_name) + .log_statements(tracing_log::log::LevelFilter::Trace) + } } impl Display for Environment { diff --git a/src/main.rs b/src/main.rs index 7a9bbdb..18482d2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -use secrecy::ExposeSecret; use sqlx::postgres::PgPoolOptions; use tokio::net::TcpListener; use zero2prod::{ @@ -12,9 +11,7 @@ async fn main() -> Result<(), std::io::Error> { let subscriber = get_subscriber("zero2prod", "info", std::io::stdout); init_subscriber(subscriber); let config = get_config().expect("Failed to read configuation."); - let pool = PgPoolOptions::new() - .connect_lazy(config.database.to_string().expose_secret()) - .expect("Failed to create Postgres connection pool."); + let pool = PgPoolOptions::new().connect_lazy_with(config.database.with_db()); let addr = format!("{}:{}", config.application.host, config.application.port); let listener = TcpListener::bind(addr) .await diff --git a/tests/health_check.rs b/tests/health_check.rs index 37dd84c..278aa2d 100644 --- a/tests/health_check.rs +++ b/tests/health_check.rs @@ -1,7 +1,6 @@ use once_cell::sync::Lazy; use reqwest::Client; -use secrecy::ExposeSecret; -use sqlx::{postgres::PgPoolOptions, Connection, Executor, PgConnection, PgPool}; +use sqlx::{Connection, Executor, PgConnection, PgPool}; use tokio::net::TcpListener; use uuid::Uuid; use zero2prod::{ @@ -29,7 +28,7 @@ async fn health_check() { async fn subscribe_returns_200_for_valid_form_data() { let app = spawn_app().await; let config = get_config().expect("Failed to read configuration."); - let mut connection = PgConnection::connect(&config.database.to_string().expose_secret()) + let mut connection = PgConnection::connect_with(&config.database.with_db()) .await .expect("Failed to connect to Postgres."); let client = Client::new(); @@ -120,7 +119,7 @@ async fn spawn_app() -> TestApp { } async fn configure_database(config: &DatabaseSettings) -> PgPool { - let mut connection = PgConnection::connect(&config.to_string_no_db().expose_secret()) + let mut connection = PgConnection::connect_with(&config.without_db()) .await .expect("Failed to connect to Postgres."); @@ -137,8 +136,7 @@ async fn configure_database(config: &DatabaseSettings) -> PgPool { .await .expect("Failed to create database."); - let pool = PgPoolOptions::new() - .connect(&config.to_string().expose_secret()) + let pool = PgPool::connect_with(config.with_db()) .await .expect("Failed to connect to Postgres.");