this repo has no description
at main 111 lines 3.9 kB view raw
1use async_trait::async_trait; 2use bb8::Pool; 3use bb8_redis::{RedisConnectionManager, redis::cmd}; 4use shared::cache::{Cache, TOWER_SESSION_KEY, create_prefixed_key}; 5use std::fmt::Display; 6use std::fmt::{Debug, Formatter}; 7use tower_sessions::SessionStore; 8use tower_sessions::session::{Id, Record}; 9use tower_sessions::session_store::{Error, Result as StoreResult}; 10 11#[derive(Clone)] 12pub struct RedisSessionStore { 13 cache_pool: Pool<RedisConnectionManager>, 14} 15 16impl RedisSessionStore { 17 pub fn new(cache_pool: Pool<RedisConnectionManager>) -> Self { 18 Self { cache_pool } 19 } 20} 21 22impl Debug for RedisSessionStore { 23 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 24 f.debug_struct("RedisSessionStore").finish() 25 } 26} 27 28// Small helper to convert any error into tower-sessions Backend error consistently 29fn backend_map<E: Display>(context: &'static str) -> impl FnOnce(E) -> Error { 30 move |err| { 31 log::error!("{}: {}", context, err); 32 Error::Backend(err.to_string()) 33 } 34} 35 36#[async_trait] 37impl SessionStore for RedisSessionStore { 38 async fn create(&self, session_record: &mut Record) -> StoreResult<()> { 39 //TODO i don't think there is an issue with overwriting the session here since it's redis and should be no collision 40 //The default create throws a warning about this so, added this to get rid of it and adding a note in case it does cause a problem 41 self.save(session_record).await 42 } 43 44 async fn save(&self, session_record: &Record) -> StoreResult<()> { 45 let id_as_str: String = session_record.id.0.to_string(); 46 let key = create_prefixed_key(TOWER_SESSION_KEY, id_as_str.as_str()); 47 48 // Get a redis connection 49 let conn = self 50 .cache_pool 51 .get() 52 .await 53 .map_err(backend_map("There was an error connecting to the cache"))?; 54 55 // Set value with TTL based on expiry_date 56 let expiry = session_record.expiry_date; 57 let now = std::time::SystemTime::now() 58 .duration_since(std::time::UNIX_EPOCH) 59 .unwrap_or_default() 60 .as_secs() as i64; 61 let ttl_secs = expiry.unix_timestamp().saturating_sub(now).max(0) as usize; 62 63 //Helper for some cache functions 64 let mut cache = Cache { redis_pool: conn }; 65 cache 66 .write_to_cache_with_seconds(&key, &session_record, ttl_secs as u64) 67 .await 68 .map_err(backend_map("There was an error saving the session"))?; 69 70 Ok(()) 71 } 72 73 async fn load(&self, session_id: &Id) -> StoreResult<Option<Record>> { 74 let id_as_str: String = session_id.0.to_string(); 75 let key = create_prefixed_key(TOWER_SESSION_KEY, id_as_str.as_str()); 76 77 let conn = self 78 .cache_pool 79 .get() 80 .await 81 .map_err(backend_map("There was an error connecting to the cache"))?; 82 let mut cache = Cache { redis_pool: conn }; 83 84 let val = match cache.fetch_redis_json_object::<Option<Record>>(&key).await { 85 Ok(Some(record)) => Ok(record), 86 Ok(None) => Ok(None), 87 Err(err) => Err(err), 88 } 89 .map_err(backend_map("There was an error loading the session"))?; 90 Ok(val) 91 } 92 93 async fn delete(&self, session_id: &Id) -> StoreResult<()> { 94 let id_as_str: String = session_id.0.to_string(); 95 let key = create_prefixed_key(TOWER_SESSION_KEY, id_as_str.as_str()); 96 97 let mut conn = self 98 .cache_pool 99 .get() 100 .await 101 .map_err(backend_map("There was an error connecting to the cache"))?; 102 103 let _: usize = cmd("DEL") 104 .arg(&key) 105 .query_async::<usize>(&mut *conn) 106 .await 107 .map_err(backend_map("There was an error deleting the session"))?; 108 109 Ok(()) 110 } 111}