spawn_access_control/
geo_analyzer.rs

1use maxminddb::{Reader, geoip2};
2use std::collections::HashMap;
3use chrono::{DateTime, Utc};
4use std::net::IpAddr;
5use serde::{Serialize, Deserialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct LocationInfo {
9    pub country: String,
10    pub city: Option<String>,
11    pub latitude: Option<f64>,
12    pub longitude: Option<f64>,
13    pub last_seen: DateTime<Utc>,
14    pub access_count: u32,
15}
16
17pub struct GeoAnalyzer {
18    db_reader: Reader<Vec<u8>>,
19    location_history: HashMap<IpAddr, LocationInfo>,
20}
21
22impl GeoAnalyzer {
23    pub fn new(db_path: &[u8]) -> Result<Self, maxminddb::MaxMindDBError> {
24        let db_bytes = db_path.to_vec();
25        Ok(Self {
26            db_reader: Reader::from_source(db_bytes)?,
27            location_history: HashMap::new(),
28        })
29    }
30
31    pub fn lookup_location(&self, ip: IpAddr) -> Option<LocationInfo> {
32        if let Ok(city) = self.db_reader.lookup::<geoip2::City>(ip) {
33            let country = city.country
34                .and_then(|c| c.names.clone())
35                .and_then(|n| n.get("en").map(|s| s.to_string()))?;
36
37            let city_name = city.city
38                .and_then(|c| c.names.clone())
39                .and_then(|n| n.get("en").map(|s| s.to_string()));
40
41            let coordinates = if let (Some(lat), Some(lon)) = (
42                city.location.as_ref().and_then(|l| l.latitude),
43                city.location.as_ref().and_then(|l| l.longitude)
44            ) {
45                Some((lat, lon))
46            } else {
47                None
48            };
49
50            Some(LocationInfo {
51                country,
52                city: city_name,
53                latitude: coordinates.map(|(lat, _)| lat),
54                longitude: coordinates.map(|(_, lon)| lon),
55                last_seen: Utc::now(),
56                access_count: 1,
57            })
58        } else {
59            None
60        }
61    }
62
63    pub fn update_location(&mut self, ip: IpAddr) -> Option<LocationInfo> {
64        if let Some(location) = self.lookup_location(ip) {
65            let entry = self.location_history.entry(ip).or_insert_with(|| location.clone());
66            entry.access_count += 1;
67            entry.last_seen = Utc::now();
68            Some(location)
69        } else {
70            None
71        }
72    }
73}