spawn_access_control/
geo_analyzer.rsuse maxminddb::{Reader, geoip2};
use std::collections::HashMap;
use chrono::{DateTime, Utc};
use std::net::IpAddr;
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LocationInfo {
pub country: String,
pub city: Option<String>,
pub latitude: Option<f64>,
pub longitude: Option<f64>,
pub last_seen: DateTime<Utc>,
pub access_count: u32,
}
pub struct GeoAnalyzer {
db_reader: Reader<Vec<u8>>,
location_history: HashMap<IpAddr, LocationInfo>,
}
impl GeoAnalyzer {
pub fn new(db_path: &[u8]) -> Result<Self, maxminddb::MaxMindDBError> {
let db_bytes = db_path.to_vec();
Ok(Self {
db_reader: Reader::from_source(db_bytes)?,
location_history: HashMap::new(),
})
}
pub fn lookup_location(&self, ip: IpAddr) -> Option<LocationInfo> {
if let Ok(city) = self.db_reader.lookup::<geoip2::City>(ip) {
let country = city.country
.and_then(|c| c.names.clone())
.and_then(|n| n.get("en").map(|s| s.to_string()))?;
let city_name = city.city
.and_then(|c| c.names.clone())
.and_then(|n| n.get("en").map(|s| s.to_string()));
let coordinates = if let (Some(lat), Some(lon)) = (
city.location.as_ref().and_then(|l| l.latitude),
city.location.as_ref().and_then(|l| l.longitude)
) {
Some((lat, lon))
} else {
None
};
Some(LocationInfo {
country,
city: city_name,
latitude: coordinates.map(|(lat, _)| lat),
longitude: coordinates.map(|(_, lon)| lon),
last_seen: Utc::now(),
access_count: 1,
})
} else {
None
}
}
pub fn update_location(&mut self, ip: IpAddr) -> Option<LocationInfo> {
if let Some(location) = self.lookup_location(ip) {
let entry = self.location_history.entry(ip).or_insert_with(|| location.clone());
entry.access_count += 1;
entry.last_seen = Utc::now();
Some(location)
} else {
None
}
}
}