lmrc_http_common/auth/
session.rs1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use uuid::Uuid;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct SessionInfo {
10 pub token: String,
12 pub user_id: String,
14 pub email: String,
16 pub created_at: DateTime<Utc>,
18 pub expires_at: DateTime<Utc>,
20 #[serde(skip_serializing_if = "Option::is_none")]
22 pub metadata: Option<serde_json::Value>,
23}
24
25impl SessionInfo {
26 pub fn new(user_id: impl Into<String>, email: impl Into<String>, expires_at: DateTime<Utc>) -> Self {
28 Self {
29 token: Uuid::new_v4().to_string(),
30 user_id: user_id.into(),
31 email: email.into(),
32 created_at: Utc::now(),
33 expires_at,
34 metadata: None,
35 }
36 }
37
38 pub fn with_token(
40 token: impl Into<String>,
41 user_id: impl Into<String>,
42 email: impl Into<String>,
43 expires_at: DateTime<Utc>,
44 ) -> Self {
45 Self {
46 token: token.into(),
47 user_id: user_id.into(),
48 email: email.into(),
49 created_at: Utc::now(),
50 expires_at,
51 metadata: None,
52 }
53 }
54
55 pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
57 self.metadata = Some(metadata);
58 self
59 }
60
61 pub fn is_expired(&self) -> bool {
63 Utc::now() > self.expires_at
64 }
65
66 pub fn remaining_seconds(&self) -> i64 {
68 (self.expires_at - Utc::now()).num_seconds().max(0)
69 }
70}
71
72#[async_trait::async_trait]
74pub trait SessionStore: Send + Sync {
75 async fn create(&self, session: &SessionInfo) -> Result<(), SessionStoreError>;
77
78 async fn get(&self, token: &str) -> Result<Option<SessionInfo>, SessionStoreError>;
80
81 async fn update(&self, session: &SessionInfo) -> Result<(), SessionStoreError>;
83
84 async fn delete(&self, token: &str) -> Result<(), SessionStoreError>;
86
87 async fn delete_user_sessions(&self, user_id: &str) -> Result<(), SessionStoreError>;
89
90 async fn cleanup_expired(&self) -> Result<u64, SessionStoreError>;
92}
93
94#[derive(Debug, thiserror::Error)]
96pub enum SessionStoreError {
97 #[error("Session not found")]
98 NotFound,
99
100 #[error("Session expired")]
101 Expired,
102
103 #[error("Database error: {0}")]
104 Database(String),
105
106 #[error("Serialization error: {0}")]
107 Serialization(String),
108
109 #[error("Internal error: {0}")]
110 Internal(String),
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116 use chrono::Duration;
117
118 #[test]
119 fn test_session_creation() {
120 let expires_at = Utc::now() + Duration::hours(1);
121 let session = SessionInfo::new("user_123", "user@example.com", expires_at);
122
123 assert_eq!(session.user_id, "user_123");
124 assert_eq!(session.email, "user@example.com");
125 assert!(!session.is_expired());
126 }
127
128 #[test]
129 fn test_session_expiration() {
130 let expires_at = Utc::now() - Duration::hours(1);
131 let session = SessionInfo::new("user_123", "user@example.com", expires_at);
132
133 assert!(session.is_expired());
134 assert_eq!(session.remaining_seconds(), 0);
135 }
136
137 #[test]
138 fn test_session_with_metadata() {
139 let expires_at = Utc::now() + Duration::hours(1);
140 let session = SessionInfo::new("user_123", "user@example.com", expires_at)
141 .with_metadata(serde_json::json!({"role": "admin"}));
142
143 assert!(session.metadata.is_some());
144 }
145}