gitdataai/libs/transport/reconnect.rs

134 lines
3.6 KiB
Rust

use redis::AsyncCommands;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClientState {
pub user_id: Uuid,
pub last_seq: HashMap<Uuid, i64>,
pub last_seen: chrono::DateTime<chrono::Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MissedMessage {
pub room_id: Uuid,
pub message_id: Uuid,
pub seq: i64,
pub content: String,
pub sender_id: Option<Uuid>,
pub send_at: chrono::DateTime<chrono::Utc>,
}
#[derive(Clone)]
pub struct ReconnectManager {
cache: db::cache::AppCache,
db: db::database::AppDatabase,
}
impl ReconnectManager {
pub fn new(cache: db::cache::AppCache, db: db::database::AppDatabase) -> Self {
Self { cache, db }
}
pub async fn save_client_state(
&self,
user_id: Uuid,
room_id: Uuid,
last_seq: i64,
) -> Result<(), crate::error::AppTransportError> {
let key = format!("client:state:{}:{}", user_id, room_id);
let value = last_seq.to_string();
let mut conn = self
.cache
.conn()
.await
.map_err(|_| crate::error::AppTransportError::Internal)?;
let _: () = conn
.set_ex(&key, &value, 86400)
.await
.map_err(|_| crate::error::AppTransportError::Internal)?;
Ok(())
}
pub async fn get_last_seq(
&self,
user_id: Uuid,
room_id: Uuid,
) -> Result<Option<i64>, crate::error::AppTransportError> {
let key = format!("client:state:{}:{}", user_id, room_id);
let mut conn = self
.cache
.conn()
.await
.map_err(|_| crate::error::AppTransportError::Internal)?;
let value: Option<String> = conn
.get(&key)
.await
.map_err(|_| crate::error::AppTransportError::Internal)?;
Ok(value.and_then(|v| v.parse::<i64>().ok()))
}
pub async fn get_missed_messages(
&self,
_user_id: Uuid,
room_id: Uuid,
since_seq: i64,
) -> Result<Vec<MissedMessage>, crate::error::AppTransportError> {
use models::rooms::room_message;
use sea_orm::*;
let messages = room_message::Entity::find()
.filter(room_message::Column::Room.eq(room_id))
.filter(room_message::Column::Seq.gt(since_seq))
.order_by_asc(room_message::Column::Seq)
.limit(100)
.all(&self.db)
.await
.map_err(|_| crate::error::AppTransportError::Internal)?;
let missed: Vec<MissedMessage> = messages
.into_iter()
.map(|m| MissedMessage {
room_id: m.room,
message_id: m.id,
seq: m.seq,
content: m.content,
sender_id: m.sender_id,
send_at: m.send_at,
})
.collect();
Ok(missed)
}
pub async fn handle_reconnect(
&self,
user_id: Uuid,
room_states: HashMap<Uuid, i64>,
) -> Result<HashMap<Uuid, Vec<MissedMessage>>, crate::error::AppTransportError> {
let mut result = HashMap::new();
for (room_id, client_seq) in room_states {
let missed = self
.get_missed_messages(user_id, room_id, client_seq)
.await?;
if !missed.is_empty() {
result.insert(room_id, missed);
}
}
Ok(result)
}
pub async fn cleanup_expired_states(&self) -> Result<(), crate::error::AppTransportError> {
Ok(())
}
}