use redis::{aio::ConnectionManager, AsyncCommands, FromRedisValue, ToRedisArgs}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; static SESSION_EXPIRATION: usize = 60 * 60 * 24; // 1 day #[derive(Clone)] pub struct SessionStorage { redis_client: ConnectionManager, } fn session_id_key(session_id: &str) -> String { format!("web::session::{}", session_id) } impl SessionStorage { pub fn new(redis_client: ConnectionManager) -> Self { Self { redis_client } } pub async fn get_user_session(&self, session_id: &str) -> Option { let key = session_id_key(session_id); let s: Result = self.redis_client.clone().get(&key).await; match s { Ok(s) => Some(s), Err(e) => { println!("redis error: {:?}", e); None }, } } pub async fn put_user_session(&self, user_session: UserSession) -> redis::RedisResult<()> { let key = session_id_key(&user_session.session_id); self .redis_client .clone() .set_options( key, user_session, redis::SetOptions::default().with_expiration(redis::SetExpiry::EX(SESSION_EXPIRATION)), ) .await } } #[derive(Debug, Serialize, Deserialize)] pub struct UserSession { session_id: String, access_token: String, refresh_token: String, } impl UserSession { pub fn new(session_id: String, access_token: String, refresh_token: String) -> Self { Self { session_id, access_token, refresh_token, } } } impl ToRedisArgs for UserSession { fn write_redis_args(&self, out: &mut W) where W: ?Sized + redis::RedisWrite, { let s = serde_json::to_string(self).unwrap(); out.write_arg(s.as_bytes()); } } impl FromRedisValue for UserSession { fn from_redis_value(v: &redis::Value) -> redis::RedisResult { let bytes = expect_redis_value_data(v)?; expect_redis_json_bytes(bytes) } } fn expect_redis_json_bytes(v: &[u8]) -> redis::RedisResult where T: DeserializeOwned, { let res: Result = serde_json::from_slice(v); match res { Ok(v) => Ok(v), Err(e) => Err(redis::RedisError::from(( redis::ErrorKind::TypeError, "redis data json deserialization failed!", e.to_string(), ))), } } fn expect_redis_value_data(v: &redis::Value) -> redis::RedisResult<&[u8]> { match v { redis::Value::Data(ref bytes) => Ok(bytes), x => Err(redis::RedisError::from(( redis::ErrorKind::TypeError, "unexpected value from redis", format!("redis value is not data: {:?}", x), ))), } }