Skip to content

Commit 32e7cb0

Browse files
chore: listen for geoip database changes
Signed-off-by: Henry Gressmann <mail@henrygressmann.de>
1 parent 5dbdae4 commit 32e7cb0

4 files changed

Lines changed: 77 additions & 39 deletions

File tree

Cargo.lock

Lines changed: 16 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,12 @@ tracing-subscriber={version="0.3", features=["env-filter"]}
4646
ahash="0.8"
4747

4848
# web
49-
poem={version="3.1", default-features=false, features=["embed", "cookie", "compression", "tower-compat"]}
49+
poem={version="3.1", default-features=false, features=[
50+
"embed",
51+
"cookie",
52+
"compression",
53+
"tower-compat",
54+
]}
5055
poem-openapi={version="5.1", default-features=false, features=["chrono"]}
5156
tower={version="0.5", default-features=false, features=["limit"]}
5257
ua-parser="0.2"

src/app/core/geoip.rs

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::net::IpAddr;
55
use std::path::{Path, PathBuf};
66
use std::sync::Arc;
77
use std::sync::atomic::{AtomicBool, Ordering};
8+
use std::time::Duration;
89

910
use crate::app::SqlitePool;
1011
use anyhow::{Context, Result, anyhow};
@@ -63,10 +64,6 @@ impl LiwanGeoIP {
6364
Ok(Self { geoip, pool, reader: ArcSwapOption::new(reader), path, downloading: Default::default() })
6465
}
6566

66-
fn is_enabled(&self) -> bool {
67-
self.reader.load().is_some() || self.downloading.load(Ordering::Acquire)
68-
}
69-
7067
fn noop(pool: SqlitePool) -> Self {
7168
Self {
7269
geoip: Default::default(),
@@ -149,30 +146,66 @@ impl LiwanGeoIP {
149146
self.downloading.store(false, Ordering::Release);
150147
Ok(())
151148
}
152-
}
153149

154-
pub fn keep_updated(geoip: Arc<LiwanGeoIP>) {
155-
if !geoip.is_enabled() {
156-
return;
150+
pub fn reload(&self) -> Result<()> {
151+
if self.downloading.load(Ordering::Acquire) {
152+
return Ok(());
153+
}
154+
155+
let reader = maxminddb::Reader::open_readfile(self.path.clone())?;
156+
self.reader.store(Some(reader.into()));
157+
Ok(())
157158
}
159+
}
158160

159-
tokio::task::spawn(async move {
160-
if let Err(e) = geoip.check_for_updates().await {
161-
tracing::error!(error = ?e, "Failed to check for GeoIP database updates");
162-
}
161+
pub async fn keep_updated(geoip: Arc<LiwanGeoIP>) {
162+
let path: PathBuf = geoip.path.clone();
163+
let mut last_meta = get_file_meta(&path);
164+
165+
let mut file_interval = tokio::time::interval(Duration::from_secs(60));
166+
let mut daily_interval = tokio::time::interval(Duration::from_secs(24 * 60 * 60));
167+
let enable_updates = geoip.geoip.maxmind_account_id.is_some() && geoip.geoip.maxmind_license_key.is_some();
163168

164-
let mut interval = tokio::time::interval(std::time::Duration::from_hours(24));
165-
loop {
166-
interval.tick().await;
167-
let geoip = geoip.clone();
168-
// Run the task once a day
169-
tokio::task::spawn(async move {
170-
if let Err(e) = geoip.check_for_updates().await {
169+
loop {
170+
tokio::select! {
171+
_ = file_interval.tick() => {
172+
// just a simple polling based file watcher so we don't need to add a bunch of dependencies
173+
let meta = get_file_meta(&path);
174+
if meta != last_meta {
175+
if let Err(e) = geoip.reload() {
176+
tracing::error!(error = ?e, "Failed to reload GeoIP database");
177+
} else {
178+
tracing::info!("GeoIP database reloaded after file change");
179+
last_meta = meta;
180+
}
181+
}
182+
}
183+
_ = daily_interval.tick() => {
184+
if enable_updates && let Err(e) = geoip.check_for_updates().await {
171185
tracing::error!(error = ?e, "Failed to check for GeoIP database updates");
172186
}
173-
});
187+
}
174188
}
175-
});
189+
}
190+
}
191+
192+
fn get_file_meta(path: &PathBuf) -> Option<(u64, u64, u64, i64)> {
193+
let md = std::fs::metadata(path).ok()?;
194+
195+
#[cfg(unix)]
196+
{
197+
use std::os::unix::fs::MetadataExt;
198+
return Some((md.dev(), md.ino(), md.size(), md.mtime()));
199+
}
200+
201+
#[cfg(windows)]
202+
{
203+
use std::os::windows::fs::MetadataExt;
204+
return Some((md.file_index(), md.volume_serial_number(), md.file_size(), md.last_write_time() as i64));
205+
};
206+
207+
#[cfg(not(any(unix, windows)))]
208+
Some((0, 0, md.len(), md.modified().ok()?.elapsed().ok()?.as_secs() as i64))
176209
}
177210

178211
async fn get_latest_md5(edition: &str, account_id: &str, license_key: &str) -> Result<String> {

src/app/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ impl Liwan {
9999
}
100100

101101
pub fn run_background_tasks(&self) {
102-
core::geoip::keep_updated(self.geoip.clone());
102+
tokio::task::spawn(core::geoip::keep_updated(self.geoip.clone()));
103103
}
104104

105105
pub fn shutdown(&self) -> Result<()> {

0 commit comments

Comments
 (0)