forked from
oppi.li/at-advent
this repo has no description
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}