use anyhow::Result; use futures::stream::TryStreamExt; use mongodb::{ bson::{doc, oid::ObjectId}, Collection, }; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize)] pub enum AuditEventType { #[serde(rename = "login_success")] LoginSuccess, #[serde(rename = "login_failed")] LoginFailed, #[serde(rename = "logout")] Logout, #[serde(rename = "password_recovery")] PasswordRecovery, #[serde(rename = "password_changed")] PasswordChanged, #[serde(rename = "account_created")] AccountCreated, #[serde(rename = "account_deleted")] AccountDeleted, #[serde(rename = "data_accessed")] DataAccessed, #[serde(rename = "data_modified")] DataModified, #[serde(rename = "data_shared")] DataShared, #[serde(rename = "session_created")] SessionCreated, #[serde(rename = "session_revoked")] SessionRevoked, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AuditLog { #[serde(rename = "_id", skip_serializing_if = "Option::is_none")] pub id: Option, pub event_type: AuditEventType, pub user_id: Option, pub email: Option, pub ip_address: String, pub resource_type: Option, pub resource_id: Option, pub timestamp: mongodb::bson::DateTime, } #[derive(Clone)] pub struct AuditLogRepository { collection: Collection, } impl AuditLogRepository { pub fn new(db: &mongodb::Database) -> Self { let collection = db.collection("audit_logs"); Self { collection } } pub async fn log( &self, event_type: AuditEventType, user_id: Option, email: Option, ip_address: String, resource_type: Option, resource_id: Option, ) -> Result { let audit_log = AuditLog { id: None, event_type, user_id, email, ip_address, resource_type, resource_id, timestamp: mongodb::bson::DateTime::now(), }; self.collection .insert_one(audit_log, 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 }, None, ) .await?; let logs: Vec = cursor.try_collect().await?; Ok(logs) } pub async fn find_recent(&self, limit: u64) -> Result> { use mongodb::options::FindOptions; let opts = FindOptions::builder() .sort(doc! { "timestamp": -1 }) .limit(limit as i64) .build(); let cursor = self.collection.find(doc! {}, opts).await?; let logs: Vec = cursor.try_collect().await?; Ok(logs) } }