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