use std::sync::Arc;
use std::time::Duration;
use tokio::sync::watch;
use tokio::task::JoinHandle;
use tokio::time;
use crate::nonce::NonceManager;
use crate::online::OnlineManager;
#[derive(Debug, Clone)]
pub struct CleanupConfig {
pub enabled: bool,
pub interval: Duration,
pub cleanup_nonce: bool,
pub cleanup_online: bool,
}
impl Default for CleanupConfig {
fn default() -> Self {
Self {
enabled: false,
interval: Duration::from_secs(300),
cleanup_nonce: true,
cleanup_online: true,
}
}
}
pub struct BackgroundCleanupTask {
stop: watch::Sender<bool>,
#[allow(dead_code)]
handle: Option<JoinHandle<()>>,
}
impl std::fmt::Debug for BackgroundCleanupTask {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("BackgroundCleanupTask { .. }")
}
}
impl BackgroundCleanupTask {
pub fn spawn(
config: CleanupConfig,
nonce: Option<Arc<NonceManager>>,
online: Option<Arc<OnlineManager>>,
) -> Self {
let (stop, rx) = watch::channel(false);
if !config.enabled {
return Self { stop, handle: None };
}
let handle = tokio::spawn(async move {
let mut ticker = time::interval(config.interval);
ticker.set_missed_tick_behavior(time::MissedTickBehavior::Delay);
let mut rx = rx;
loop {
tokio::select! {
_ = ticker.tick() => {
if config.cleanup_nonce {
if let Some(n) = &nonce {
if let Err(e) = n.cleanup_expired().await {
tracing::warn!(error = %e, "nonce cleanup failed");
}
}
}
if config.cleanup_online {
if let Some(o) = &online {
match o.get_online_users().await {
Ok(users) => {
for uid in users {
if let Err(e) = o.get_user_sessions(&uid).await {
tracing::warn!(error = %e, login_id = %uid, "online prune failed");
}
}
}
Err(e) => tracing::warn!(error = %e, "online list failed during cleanup"),
}
}
}
}
_ = rx.changed() => {
if *rx.borrow() {
break;
}
}
}
}
});
Self {
stop,
handle: Some(handle),
}
}
pub fn shutdown(&self) {
let _ = self.stop.send(true);
}
}
impl Drop for BackgroundCleanupTask {
fn drop(&mut self) {
let _ = self.stop.send(true);
}
}