Commit Diff


commit - 1cb08214cf3a82ad3acb8fec023e7fb2a404f6bd
commit + c806356b760c9478ac6b47e22e71917ba436bcd8
blob - 556952c4198c548f93bd7667434b18fda7be9c84
blob + 9b0bebcc211a37fb3c319e992a6b8b8919de1f93
--- web/README.md
+++ web/README.md
@@ -4,7 +4,7 @@
 * [x] Graceful Shutdown
 * [x] Minijinja
 * [x] Prometheus
-* [ ] Middleware Ip
+* [x] Middleware Ip
 * [x] Request Id Header
 * [x] Static files
 * [x] Config
blob - 207eafbd53830c5193ccc5ea5502abca1654ccd8
blob + d266dac95ef710f4e07d61d0565c34bc52ca0566
--- web/template/Cargo.toml
+++ web/template/Cargo.toml
@@ -9,6 +9,7 @@ edition = "2024"
 [dependencies]
 anyhow = "=1.0.100"
 axum = { version = "=0.8.6", features = ["macros"] }
+axum-client-ip = "=1.1.3"
 axum-messages = "=0.8.0"
 axum_csrf = { version = "=0.11.0", features = ["layer"] }
 config = { version = "=0.15.19", default-features = false, features = ["toml"] }
blob - f9c63402ebb48560a26b3ab2a1b4286a335395f8
blob + d1f56d93282c97bddded2b3c6399390911d2364c
--- web/template/src/main.rs
+++ web/template/src/main.rs
@@ -14,6 +14,7 @@
 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 //
 
+use std::net::SocketAddr;
 use std::sync::Arc;
 
 use minijinja::Environment;
@@ -23,8 +24,8 @@ use tracing::info;
 mod helpers;
 mod metric;
 mod router;
-mod state;
 mod settings;
+mod state;
 
 #[tokio::main]
 async fn main() -> anyhow::Result<()> {
@@ -52,8 +53,11 @@ async fn start_main_server() -> anyhow::Result<()> {
     // TODO(msi): from config
     let listener = TcpListener::bind("0.0.0.0:3000").await?;
     info!("listening on http://{}", listener.local_addr().unwrap());
-    axum::serve(listener, app)
-        .with_graceful_shutdown(helpers::shutdown_signal())
-        .await?;
+    axum::serve(
+        listener,
+        app.into_make_service_with_connect_info::<SocketAddr>(),
+    )
+    .with_graceful_shutdown(helpers::shutdown_signal())
+    .await?;
     Ok(())
 }
blob - 170890ae66c2802d5e4c9638ba179ea083e46c7b
blob + ca2f2a800fa47ac104c61617c1a9d127a84700b4
--- web/template/src/router.rs
+++ web/template/src/router.rs
@@ -23,6 +23,7 @@ use axum::{
     response::{Html, IntoResponse, Redirect},
     routing::get,
 };
+use axum_client_ip::{ClientIp, ClientIpSource};
 use axum_csrf::{CsrfConfig, CsrfLayer, CsrfToken, Key};
 use axum_messages::{Messages, MessagesManagerLayer};
 use minijinja::context;
@@ -62,6 +63,9 @@ pub(crate) fn route(app_state: Arc<AppState>) -> Route
         .with_key(Some(cookie_key))
         .with_cookie_domain(Some("127.0.0.1"));
 
+    // TODO(msi): from config, if debug mode
+    let ip_source = ClientIpSource::ConnectInfo;
+
     Router::new()
         .route("/", get(handler_home))
         .route("/content", get(handler_content))
@@ -70,6 +74,7 @@ pub(crate) fn route(app_state: Arc<AppState>) -> Route
         .route("/message", get(set_messages_handler))
         .route("/read-messages", get(read_messages_handler))
         .route("/csrf", get(csrf_root).post(csrf_check_key))
+        .route("/ip", get(ip_handler))
         .layer(MessagesManagerLayer)
         // TODO(msi): from config folder asssets
         .nest_service("/assets", ServeDir::new("assets"))
@@ -97,6 +102,7 @@ pub(crate) fn route(app_state: Arc<AppState>) -> Route
                 .with_expiry(Expiry::OnInactivity(Duration::seconds(10))),
             MessagesManagerLayer,
             CsrfLayer::new(config),
+            ip_source.into_extension(),
             // TODO(msi): from config
             TimeoutLayer::new(std::time::Duration::from_secs(10)),
             PropagateRequestIdLayer::new(x_request_id),
@@ -106,6 +112,10 @@ pub(crate) fn route(app_state: Arc<AppState>) -> Route
         .with_state(app_state)
 }
 
+async fn ip_handler(ClientIp(ip): ClientIp) -> String {
+    ip.to_string()
+}
+
 async fn csrf_root(
     token: CsrfToken,
     State(state): State<Arc<AppState>>,
blob - 897cbf4288d8cc11fc05ceb140d88314764f74bd
blob + 981476489b4c549916d998d1ff06ed29398442de
--- web/template/src/settings.rs
+++ web/template/src/settings.rs
@@ -48,7 +48,8 @@ pub(crate) struct Settings {
 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 run_mode =
+            env::var("RUN_MODE").unwrap_or_else(|_| "development".into());
 
         let s = Config::builder()
             // Start off by merging in the "default" configuration file
@@ -57,8 +58,7 @@ impl Settings {
             // Default to 'development' env
             // Note that this file is _optional_
             .add_source(
-                File::with_name(&format!("config/{run_mode}"))
-                    .required(false),
+                File::with_name(&format!("config/{run_mode}")).required(false),
             )
             // Add in a local configuration file
             // This file shouldn't be checked in to git
blob - f162c30c552c43988a707bd39c1cbd88cc1ed4a4
blob + 6abc2fda11620fbecb3d72b3b37cc2b3dac02d2d
--- web/template/templates/layout.jinja
+++ web/template/templates/layout.jinja
@@ -12,6 +12,7 @@
             <li><a href="/message">Set Message</a></li>
             <li><a href="/read-messages">Read Messages</a></li>
             <li><a href="/csrf">Csrf</a></li>
+            <li><a href="/ip">Ip</a></li>
         </ul>
     </nav>
     <h1><h1>Hello, World web =]</h1>