Commit Diff


commit - 7583df586ed40b9202e7585fa5fd795ab2e3315b
commit + 1cb08214cf3a82ad3acb8fec023e7fb2a404f6bd
blob - 21363f3dede514363d423a5be1011fbf372824a2
blob + 556952c4198c548f93bd7667434b18fda7be9c84
--- web/README.md
+++ web/README.md
@@ -7,7 +7,7 @@
 * [ ] Middleware Ip
 * [x] Request Id Header
 * [x] Static files
-* [ ] Config
+* [x] Config
 * [x] Tracing
 * [x] Messages (like flask)
 * [x] Sessions
blob - 2f7896d1d1365eafb0da03d9fe456fac81408487
blob + ce8c5ca8cfa7766e7cf52af12119b6ed76a18e38
--- web/template/.gitignore
+++ web/template/.gitignore
@@ -1 +1,2 @@
 target/
+config/production.toml
blob - bfb4153d79077a8e152f7d143adf4d4002ed7a39
blob + 207eafbd53830c5193ccc5ea5502abca1654ccd8
--- web/template/Cargo.toml
+++ web/template/Cargo.toml
@@ -9,8 +9,9 @@ edition = "2024"
 [dependencies]
 anyhow = "=1.0.100"
 axum = { version = "=0.8.6", features = ["macros"] }
-axum-messages = "0.8.0"
-axum_csrf = { version = "0.11.0", features = ["layer"] }
+axum-messages = "=0.8.0"
+axum_csrf = { version = "=0.11.0", features = ["layer"] }
+config = { version = "=0.15.19", default-features = false, features = ["toml"] }
 metrics = { version = "=0.24.2", default-features = false }
 metrics-exporter-prometheus = { version = "=0.17.2", default-features = false }
 minijinja = "=2.12.0"
@@ -21,4 +22,3 @@ tower-http = { version = "=0.6.6", features = ["timeou
 tower-sessions = "=0.14.0"
 tracing = "=0.1.41"
 tracing-subscriber = { version = "=0.3.20", features = ["env-filter"] }
-
blob - /dev/null
blob + dbb2f3020990f9cb884e02408ce2471f3e481bbb (mode 644)
--- /dev/null
+++ web/template/config/default.toml
@@ -0,0 +1,17 @@
+[database]
+url = "postgres://postgres@localhost"
+
+[sparkpost]
+key = "sparkpost-dev-key"
+token = "sparkpost-dev-token"
+url = "https://api.sparkpost.com"
+version = 1
+
+[twitter]
+consumer_token = "twitter-dev-consumer-key"
+consumer_secret = "twitter-dev-consumer-secret"
+
+[braintree]
+merchant_id = "braintree-merchant-id"
+public_key = "braintree-dev-public-key"
+private_key = "braintree-dev-private-key"
blob - /dev/null
blob + f07dcc2b400cf54b26e71ab75acb270f891f79c5 (mode 644)
--- /dev/null
+++ web/template/config/development.toml
@@ -0,0 +1,4 @@
+debug = true
+
+[database]
+echo = true
blob - 315a365408046be29f9ce0bf5eac5637a77bb219
blob + f9c63402ebb48560a26b3ab2a1b4286a335395f8
--- web/template/src/main.rs
+++ web/template/src/main.rs
@@ -24,11 +24,14 @@ mod helpers;
 mod metric;
 mod router;
 mod state;
+mod settings;
 
 #[tokio::main]
 async fn main() -> anyhow::Result<()> {
     helpers::init_tracing();
 
+    let _settings = settings::Settings::new();
+
     let (_main_server, _metrics_server) =
         tokio::join!(start_main_server(), metric::start_metrics_server());
     Ok(())
blob - /dev/null
blob + 897cbf4288d8cc11fc05ceb140d88314764f74bd (mode 644)
--- /dev/null
+++ web/template/src/settings.rs
@@ -0,0 +1,80 @@
+use std::env;
+
+use tracing::{debug, info};
+
+use config::{Config, ConfigError, Environment, File};
+use serde::Deserialize;
+
+#[derive(Debug, Deserialize)]
+#[allow(unused)]
+struct Database {
+    url: String,
+}
+
+#[derive(Debug, Deserialize)]
+#[allow(unused)]
+struct Sparkpost {
+    key: String,
+    token: String,
+    url: String,
+    version: u8,
+}
+
+#[derive(Debug, Deserialize)]
+#[allow(unused)]
+struct Twitter {
+    consumer_token: String,
+    consumer_secret: String,
+}
+
+#[derive(Debug, Deserialize)]
+#[allow(unused)]
+struct Braintree {
+    merchant_id: String,
+    public_key: String,
+    private_key: String,
+}
+
+#[derive(Debug, Deserialize)]
+#[allow(unused)]
+pub(crate) struct Settings {
+    debug: bool,
+    database: Database,
+    sparkpost: Sparkpost,
+    twitter: Twitter,
+    braintree: Braintree,
+}
+
+impl Settings {
+    pub(crate) fn new() -> Result<Self, ConfigError> {
+        info!("loading settings");
+        let run_mode = env::var("RUN_MODE").unwrap_or_else(|_| "development".into());
+
+        let s = Config::builder()
+            // Start off by merging in the "default" configuration file
+            .add_source(File::with_name("config/default"))
+            // Add in the current environment file
+            // Default to 'development' env
+            // Note that this file is _optional_
+            .add_source(
+                File::with_name(&format!("config/{run_mode}"))
+                    .required(false),
+            )
+            // Add in a local configuration file
+            // This file shouldn't be checked in to git
+            .add_source(File::with_name("config/local").required(false))
+            // Add in settings from the environment (with a prefix of APP)
+            // Eg.. `APP_DEBUG=1 ./target/app` would set the `debug` key
+            .add_source(Environment::with_prefix("app"))
+            // You may also programmatically change settings
+            .set_override("database.url", "postgres://")?
+            .build()?;
+
+        // Now that we're done, let's access our configuration
+        debug!("debug: {:?}", s.get_bool("debug"));
+        debug!("database: {:?}", s.get::<String>("database.url"));
+
+        // You can deserialize (and thus freeze) the entire configuration as
+        s.try_deserialize()
+    }
+}