#![allow(dead_code)] #![allow(unused_imports)] use anyhow::Result; use futures::stream::TryStreamExt; use mongodb::{ bson::{doc, oid::ObjectId}, Collection, }; use serde::{Deserialize, Serialize}; use std::time::SystemTime; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DeviceInfo { pub device_type: String, // "mobile", "desktop", "tablet" pub os: String, // "iOS", "Android", "Windows", "macOS", "Linux" pub browser: Option, pub ip_address: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Session { #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] pub id: Option, pub user_id: ObjectId, pub device_info: DeviceInfo, pub token_hash: String, // Hash of the JWT token pub created_at: mongodb::bson::DateTime, pub last_used_at: mongodb::bson::DateTime, pub expires_at: mongodb::bson::DateTime, pub is_revoked: bool, } #[derive(Clone)] pub struct SessionRepository { collection: Collection, } impl SessionRepository { pub fn new(db: &mongodb::Database) -> Self { let collection = db.collection("sessions"); Self { collection } } pub async fn create( &self, user_id: ObjectId, device_info: DeviceInfo, token_hash: String, duration_hours: i64, ) -> Result { let now = SystemTime::now(); let now_bson = mongodb::bson::DateTime::from(now); let expires_at = SystemTime::now() .checked_add(std::time::Duration::from_secs(duration_hours as u64 * 3600)) .ok_or_else(|| anyhow::anyhow!("Invalid duration"))?; let expires_at_bson = mongodb::bson::DateTime::from(expires_at); let session = Session { id: None, user_id, device_info, token_hash, created_at: now_bson, last_used_at: now_bson, expires_at: expires_at_bson, is_revoked: false, }; self.collection .insert_one(session, None) .await? .inserted_id .as_object_id() .ok_or_else(|| anyhow::anyhow!("Failed to get inserted id")) } pub async fn find_by_user(&self, user_id: &ObjectId) -> Result> { let cursor = self .collection .find( doc! { "user_id": user_id, "is_revoked": false, "expires_at": { "$gt": mongodb::bson::DateTime::now() } }, None, ) .await?; let sessions: Vec = cursor.try_collect().await?; Ok(sessions) } pub async fn revoke(&self, session_id: &ObjectId) -> Result<()> { self.collection .update_one( doc! { "_id": session_id }, doc! { "$set": { "is_revoked": true } }, None, ) .await?; Ok(()) } pub async fn revoke_all_for_user(&self, user_id: &ObjectId) -> Result<()> { self.collection .update_many( doc! { "user_id": user_id, "is_revoked": false }, doc! { "$set": { "is_revoked": true } }, None, ) .await?; Ok(()) } pub async fn cleanup_expired(&self) -> Result { let result = self .collection .delete_many( doc! { "expires_at": { "$lt": mongodb::bson::DateTime::now() } }, None, ) .await?; Ok(result.deleted_count) } }