modo/geolocation/mod.rs
1//! # modo::geolocation
2//!
3//! IP-to-location lookup using a MaxMind GeoLite2/GeoIP2 `.mmdb` database.
4//!
5//! ## Provides
6//!
7//! - [`GeolocationConfig`] — YAML-deserializable config with `mmdb_path`
8//! - [`GeoLocator`] — MaxMind database reader; cheaply cloneable via `Arc`
9//! - [`GeoLayer`] — Tower layer that resolves location per request; also
10//! re-exported as [`modo::middlewares::Geo`](crate::middlewares::Geo)
11//! - [`Location`] — resolved geolocation data; doubles as an axum extractor
12//!
13//! ## Quick start
14//!
15//! 1. Configure `mmdb_path` in the `geolocation` section of your YAML config.
16//! 2. Build a [`GeoLocator`] at startup with [`GeoLocator::from_config`].
17//! 3. Add [`ClientIpLayer`](crate::ip::ClientIpLayer) **before** [`GeoLayer`]
18//! in your middleware stack so client IP resolution happens first.
19//! 4. Use [`Location`] as an axum extractor in handlers to read the resolved
20//! geolocation for each request.
21//!
22//! ```rust,ignore
23//! let config = GeolocationConfig { mmdb_path: "data/GeoLite2-City.mmdb".into() };
24//! let locator = GeoLocator::from_config(&config)?;
25//!
26//! let app = Router::new()
27//! .route("/", get(handler))
28//! .layer(GeoLayer::new(locator))
29//! .layer(ClientIpLayer::new());
30//!
31//! async fn handler(location: Location) -> String {
32//! format!("country: {:?}", location.country_code)
33//! }
34//! ```
35
36mod config;
37mod location;
38mod locator;
39mod middleware;
40
41pub use config::GeolocationConfig;
42pub use location::Location;
43pub use locator::GeoLocator;
44pub use middleware::GeoLayer;