use std::collections::HashMap; use uuid::Uuid; use crate::cache::redis::AppRedis; use crate::error::{AppError, AppResult}; use ::redis::Cmd; use super::redis_keys::*; pub struct ReconnectManager { redis: AppRedis, } impl ReconnectManager { pub fn new(redis: AppRedis) -> Self { Self { redis } } pub fn save_read_position(&self, user_id: Uuid, channel_id: Uuid, seq: i64) -> AppResult<()> { let key = format!("{WS_RECONNECT_PREFIX}{user_id}:{channel_id}"); let mut conn = self.redis.get_connection()?; Cmd::new() .arg("SETEX") .arg(&key) .arg(WS_RECONNECT_STATE_TTL_SECS) .arg(seq.to_string()) .query::<()>(&mut *conn.inner_mut()) .map_err(AppError::Redis)?; Ok(()) } pub fn save_read_positions( &self, user_id: Uuid, positions: &HashMap, ) -> AppResult<()> { let mut conn = self.redis.get_connection()?; for (channel_id, seq) in positions { let key = format!("{WS_RECONNECT_PREFIX}{user_id}:{channel_id}"); Cmd::new() .arg("SETEX") .arg(&key) .arg(WS_RECONNECT_STATE_TTL_SECS) .arg(seq.to_string()) .query::<()>(&mut *conn.inner_mut()) .map_err(AppError::Redis)?; } Ok(()) } pub fn get_last_seq(&self, user_id: Uuid, channel_id: Uuid) -> AppResult> { let key = format!("{WS_RECONNECT_PREFIX}{user_id}:{channel_id}"); let mut conn = self.redis.get_connection()?; let val: Option = Cmd::new() .arg("GET") .arg(&key) .query(&mut *conn.inner_mut()) .map_err(AppError::Redis)?; Ok(val.and_then(|v| v.parse().ok())) } pub fn get_all_positions(&self, user_id: Uuid) -> AppResult> { let pattern = format!("{WS_RECONNECT_PREFIX}{user_id}:*"); let mut conn = self.redis.get_connection()?; let keys: Vec = Cmd::new() .arg("KEYS") .arg(&pattern) .query(&mut *conn.inner_mut()) .map_err(AppError::Redis)?; let mut result = HashMap::new(); let prefix_len = format!("{WS_RECONNECT_PREFIX}{user_id}:").len(); for key in &keys { if let Some(channel_str) = key.get(prefix_len..) && let Ok(channel_id) = channel_str.parse::() { let val: Option = Cmd::new() .arg("GET") .arg(key) .query(&mut *conn.inner_mut()) .map_err(AppError::Redis)?; if let Some(v) = val && let Ok(seq) = v.parse::() { result.insert(channel_id, seq); } } } Ok(result) } pub fn cleanup_channel(&self, user_id: Uuid, channel_id: Uuid) -> AppResult<()> { let key = format!("{WS_RECONNECT_PREFIX}{user_id}:{channel_id}"); let mut conn = self.redis.get_connection()?; let _ = Cmd::new() .arg("DEL") .arg(&key) .query::<()>(&mut *conn.inner_mut()); Ok(()) } }