134 lines
3.6 KiB
Rust
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(())
|
|
}
|
|
}
|