axum-rh 0.2.8

A helper library for the axum router
Documentation
use axum::{
    extract::FromRequestParts,
    http::{request::Parts, StatusCode},
};
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use tower_sessions::Session;

pub trait SessionTrait: Send + Sync + Serialize + for<'de> Deserialize<'de> + Default {
    fn key(&self) -> Option<String>;
    fn set_key(&mut self, key: String);
    fn has_key(&self) -> bool {
        self.key().is_some()
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SessionData {
    pub user_id: Option<String>,
    pub created_at: OffsetDateTime,
    pub end_at: OffsetDateTime,
}

impl SessionTrait for SessionData {
    fn key(&self) -> Option<String> {
        self.user_id.clone()
    }

    fn set_key(&mut self, key: String) {
        self.user_id = Some(key);
    }
}

impl Default for SessionData {
    fn default() -> Self {
        Self {
            user_id: None,
            created_at: OffsetDateTime::now_utc(),
            end_at: OffsetDateTime::now_utc(),
        }
    }
}
pub struct SessionObject<T>
where
    T: SessionTrait,
{
    pub session: Session,
    pub data: T,
}

impl<T> SessionObject<T>
where
    T: SessionTrait,
{
    const DATA_KEY: &'static str = "data";

    pub async fn update(&mut self) {
        Self::update_session(&self.session, &self.data).await;
    }

    pub async fn update_key(&mut self, key: String) {
        self.data.set_key(key);
        self.update().await;
    }

    async fn update_session(session: &Session, session_data: &T) {
        match session.insert(Self::DATA_KEY, session_data).await {
            Ok(_) => {}
            Err(e) => {
                log::error!("Error updating session: {:?}", e);
            }
        };
        match session.save().await {
            Ok(_) => {}
            Err(e) => {
                log::error!("Error saving session: {:?}", e);
            }
        };
    }

    pub async fn clear(&mut self) {
        self.data = T::default();
        self.session.clear().await;
    }
}

impl<S, T> FromRequestParts<S> for SessionObject<T>
where
    S: Send + Sync,
    T: SessionTrait,
{
    type Rejection = (StatusCode, &'static str);

    async fn from_request_parts(req: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
        let session = Session::from_request_parts(req, state).await?;

        let data: T = session
            .get(Self::DATA_KEY)
            .await
            .expect("session data not found")
            .unwrap_or_default();

        Self::update_session(&session, &data).await;
        Ok(Self { session, data })
    }
}