use super::device_registry::{is_device_record_expired, offline_device_expires_at_ms};
use super::native_signaling::{DeviceFields, NativeFirestoreSignalingBackend};
use anyhow::Result;
impl NativeFirestoreSignalingBackend {
pub(super) async fn sweep_stale_devices(
&self,
user_id: &str,
stale_after_ms: i64,
) -> Result<usize> {
let parent = self.user_parent(user_id);
let parent_for_sweep = parent.clone();
let expected_tag = self.app_tag().to_string();
let stale_after_ms = stale_after_ms.max(0);
self.with_auth_retry(move |db| {
let parent = parent_for_sweep.clone();
let expected_tag = expected_tag.clone();
async move {
let docs = db
.fluent()
.select()
.from("devices")
.parent(&parent)
.query()
.await?;
let now_ms = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)?
.as_millis() as i64;
let stale_doc_ids: Vec<String> = docs
.into_iter()
.filter_map(|doc| {
let id = doc.name.split('/').last().unwrap_or("").to_string();
let fields = doc.fields;
let shared = Self::shared_device_record_from_fields(&fields);
if shared.effective_app_tag() != Some(expected_tag.as_str()) {
return None;
}
if !shared.online {
return None;
}
if is_device_record_expired(&shared, now_ms, stale_after_ms) {
return Some(id);
}
None
})
.collect();
if stale_doc_ids.is_empty() {
return Ok(0usize);
}
let offline_update = DeviceFields {
app_tag: None,
user_id: None,
device_id: None,
device_name: None,
node_id: None,
platform_type: None,
session_id: None,
tag: None,
kind: None,
capabilities: None,
metadata: None,
online: Some(false),
ticket: None,
last_seen_at: None,
updated_at: Some(now_ms),
created_at: None,
expires_at: Some(offline_device_expires_at_ms(now_ms)),
excluded_peers: vec![],
};
for doc_id in &stale_doc_ids {
db.fluent()
.update()
.fields(vec!["online", "updatedAt", "expiresAt"])
.in_col("devices")
.document_id(doc_id)
.parent(&parent)
.object(&offline_update)
.execute::<()>()
.await?;
}
Ok(stale_doc_ids.len())
}
})
.await
}
}