use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Session {
pub id: String,
pub data: HashMap<String, serde_json::Value>,
pub created_at: u64,
pub accessed_at: u64,
pub expires_at: u64,
}
impl Session {
pub fn new(id: String, ttl: Duration) -> Self {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
Self {
id,
data: HashMap::new(),
created_at: now,
accessed_at: now,
expires_at: now + ttl.as_secs(),
}
}
pub fn is_expired(&self) -> bool {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
now > self.expires_at
}
pub fn get(&self, key: &str) -> Option<&serde_json::Value> {
self.data.get(key)
}
pub fn set(&mut self, key: &str, value: serde_json::Value) {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
self.accessed_at = now;
self.data.insert(key.to_string(), value);
}
pub fn remove(&mut self, key: &str) {
self.data.remove(key);
}
}
pub struct SessionStore {
sessions: Mutex<HashMap<String, Session>>,
}
impl SessionStore {
pub fn new() -> Self {
Self {
sessions: Mutex::new(HashMap::new()),
}
}
pub async fn create(&self, session: &Session) -> Result<(), SessionError> {
let mut sessions = self.sessions.lock().unwrap();
sessions.insert(session.id.clone(), session.clone());
Ok(())
}
pub async fn get(&self, id: &str) -> Result<Option<Session>, SessionError> {
let sessions = self.sessions.lock().unwrap();
Ok(sessions.get(id).cloned().filter(|s| !s.is_expired()))
}
pub async fn update(&self, session: &Session) -> Result<(), SessionError> {
let mut sessions = self.sessions.lock().unwrap();
sessions.insert(session.id.clone(), session.clone());
Ok(())
}
pub async fn delete(&self, id: &str) -> Result<(), SessionError> {
let mut sessions = self.sessions.lock().unwrap();
sessions.remove(id);
Ok(())
}
pub async fn touch(&self, id: &str) -> Result<(), SessionError> {
let mut sessions = self.sessions.lock().unwrap();
if let Some(session) = sessions.get_mut(id) {
session.accessed_at = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
}
Ok(())
}
}
impl Default for SessionStore {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct SessionConfig {
pub name: String,
pub ttl: Duration,
pub secure: bool,
pub http_only: bool,
pub same_site: String,
}
impl Default for SessionConfig {
fn default() -> Self {
Self {
name: "kegani_session".to_string(),
ttl: Duration::from_secs(3600 * 24),
secure: true,
http_only: true,
same_site: "lax".to_string(),
}
}
}
#[derive(Debug)]
pub enum SessionError {
NotFound,
Expired,
LockFailed,
}
impl std::fmt::Display for SessionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SessionError::NotFound => write!(f, "Session not found"),
SessionError::Expired => write!(f, "Session expired"),
SessionError::LockFailed => write!(f, "Failed to acquire session lock"),
}
}
}
impl std::error::Error for SessionError {}