diff --git a/Cargo.lock b/Cargo.lock index 26a8c6f..6a65338 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -985,9 +985,11 @@ dependencies = [ "actix-web", "chrono", "config", + "log", "once_cell", "reqwest", "serde 1.0.130", + "serde-aux", "sqlx", "tokio", "tracing", @@ -1605,6 +1607,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-aux" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93abf9799c576f004252b2a05168d58527fb7c54de12e94b4d12fe3475ffad24" +dependencies = [ + "chrono", + "serde 1.0.130", + "serde_json", +] + [[package]] name = "serde-hjson" version = "0.9.1" diff --git a/Cargo.toml b/Cargo.toml index 0230fe0..d1caa54 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,8 @@ tracing-subscriber = { version = "0.2.12", features = ["registry", "env-filter"] tracing-bunyan-formatter = "0.1.6" tracing-log = "0.1.1" tracing-actix-web = "0.4.0-beta.12" +serde-aux = "3.0.1" +log = "0.4" [dependencies.sqlx] version = "0.5.7" diff --git a/configuration/local.yaml b/configuration/local.yaml index 7176c31..3b77405 100644 --- a/configuration/local.yaml +++ b/configuration/local.yaml @@ -1,2 +1,4 @@ application: - host: 127.0.0.1 \ No newline at end of file + host: 127.0.0.1 +database: + require_ssl: false \ No newline at end of file diff --git a/configuration/production.yaml b/configuration/production.yaml index 271bebd..f3ac210 100644 --- a/configuration/production.yaml +++ b/configuration/production.yaml @@ -1,2 +1,4 @@ application: - host: 0.0.0.0 \ No newline at end of file + host: 0.0.0.0 +database: + require_ssl: true \ No newline at end of file diff --git a/spec.yaml b/spec.yaml new file mode 100644 index 0000000..d44f235 --- /dev/null +++ b/spec.yaml @@ -0,0 +1,41 @@ +#! spec.yaml +name: rust-project + +region: sgp1 +services: + - name: rust-project + dockerfile_path: Dockerfile + source_dir: . + github: + repo: NickBland/mailApp + branch: master + deploy_on_push: true + health_check: + http_path: /health_check + http_port: 8000 + instance_count: 1 + instance_size_slug: basic-xxs + routes: + - path: / + 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} +databases: + - engine: PG + name: newsletter + num_nodes: 1 + size: deb-s-dev-database + version: "12" \ No newline at end of file diff --git a/src/configuration.rs b/src/configuration.rs index 97addb1..df81334 100644 --- a/src/configuration.rs +++ b/src/configuration.rs @@ -1,5 +1,8 @@ use std::convert::{TryFrom, TryInto}; +use serde_aux::field_attributes::deserialize_number_from_string; +use sqlx::postgres::{PgConnectOptions, PgSslMode}; + #[derive(serde::Deserialize)] pub struct Settings { pub database: DatabaseSettings, @@ -8,6 +11,7 @@ pub struct Settings { #[derive(serde::Deserialize)] pub struct ApplicationSettings { + #[serde(deserialize_with = "deserialize_number_from_string")] pub port: u16, pub host: String, } @@ -16,9 +20,11 @@ pub struct ApplicationSettings { pub struct DatabaseSettings { pub username: String, pub password: String, + #[serde(deserialize_with = "deserialize_number_from_string")] pub port: u16, pub host: String, pub database_name: String, + pub require_ssl: bool, } pub fn get_configuration() -> Result { @@ -36,7 +42,7 @@ pub fn get_configuration() -> Result { .expect("Failed to parse APP_ENVIRONMENT."); settings.merge( - config::File::from(configuration_directory.join(environment.as_str())).required(true) + config::File::from(configuration_directory.join(environment.as_str())).required(true), )?; settings.merge(config::Environment::with_prefix("app").separator("__"))?; @@ -74,17 +80,21 @@ impl TryFrom for Environment { } impl DatabaseSettings { - pub fn connection_string(&self) -> String { - format!( - "postgres://{}:{}@{}:{}/{}", - self.username, self.password, self.host, self.port, self.database_name - ) + 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) + .port(self.port) + .ssl_mode(ssl_mode) } - pub fn connection_string_without_db(&self) -> String { - format!( - "postgres://{}:{}@{}:{}", - self.username, self.password, self.host, self.port - ) + pub fn with_db(&self) -> PgConnectOptions { + self.without_db().database(&self.database_name).log_statements(log::LevelFilter::Trace) } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 2716a87..6e5651a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,9 +16,7 @@ async fn main() -> std::io::Result<()> { // Configure connection to database for our startup let connection_pool = PgPoolOptions::new() .connect_timeout(std::time::Duration::from_secs(2)) - .connect(&configuration.database.connection_string()) - .await - .expect("Failed to connect to Postgres."); + .connect_lazy_with(configuration.database.with_db()); // Take port from settings file let address = format!("{}:{}", configuration.application.host, configuration.application.port); diff --git a/tests/health_check.rs b/tests/health_check.rs index a446eba..6be38a2 100644 --- a/tests/health_check.rs +++ b/tests/health_check.rs @@ -49,16 +49,16 @@ async fn spawn_app() -> TestApp { pub async fn configure_database(config: &DatabaseSettings) -> PgPool { // Create database - let mut connection = PgConnection::connect(&config.connection_string_without_db()) + let mut connection = PgConnection::connect(&config.without_db()) .await .expect("Failed to connect to Postgres"); connection - .execute(&*format!(r#"CREATE DATABASE "{}";"#, config.database_name)) + .execute(&*format!(r#"CREATE DATABASE "{}";"#, config.database_name).as_str()) .await .expect("Failed to create database."); // Migrate database - let connection_pool = PgPool::connect(&config.connection_string()) + let connection_pool = PgPool::connect(config.with_db()) .await .expect("Failed to connect to Postgres."); sqlx::migrate!("./migrations")