use async_trait::async_trait;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::domain::entity::AccessLog;
use crate::infrastructure::persistence::AccessLogRepository;
use super::CommandHandler;
#[deprecated(since = "0.2.0", note = "Use CommandHandler<C> trait instead")]
#[async_trait]
pub trait AccessLogCommand: Send + Sync {
type Output;
async fn execute(&self) -> Result<Self::Output>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateAccessLogCommand {
pub file_id: Uuid,
pub bucket_id: Option<Uuid>,
pub action: AccessAction,
pub user_id: Option<Uuid>,
pub share_id: Option<Uuid>,
pub is_owner: bool,
pub is_shared: bool,
pub is_public: bool,
pub ip_address: Option<String>,
pub user_agent: Option<String>,
pub referer: Option<String>,
pub country_code: Option<String>,
pub city: Option<String>,
pub bytes_transferred: Option<i64>,
pub duration_ms: Option<i32>,
pub success: bool,
pub error_message: Option<String>,
pub accessed_at: DateTime<Utc>,
pub metadata: serde_json::Value,
}
pub struct CreateAccessLogHandler<R: AccessLogRepository> {
repository: std::sync::Arc<R>,
}
impl<R: AccessLogRepository> CreateAccessLogHandler<R> {
pub fn new(repository: std::sync::Arc<R>) -> Self {
Self { repository }
}
}
#[async_trait]
impl<R: AccessLogRepository + 'static> CommandHandler<CreateAccessLogCommand> for CreateAccessLogHandler<R> {
type Output = AccessLog;
async fn handle(&self, cmd: CreateAccessLogCommand) -> Result<Self::Output> {
let entity = AccessLog::builder()
.id(Uuid::new_v4().to_string())
.file_id(cmd.file_id)
.bucket_id(cmd.bucket_id)
.action(cmd.action)
.user_id(cmd.user_id)
.share_id(cmd.share_id)
.is_owner(cmd.is_owner)
.is_shared(cmd.is_shared)
.is_public(cmd.is_public)
.ip_address(cmd.ip_address)
.user_agent(cmd.user_agent)
.referer(cmd.referer)
.country_code(cmd.country_code)
.city(cmd.city)
.bytes_transferred(cmd.bytes_transferred)
.duration_ms(cmd.duration_ms)
.success(cmd.success)
.error_message(cmd.error_message)
.accessed_at(cmd.accessed_at)
.metadata(cmd.metadata)
.build()?;
self.repository.save(&entity).await
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateAccessLogCommand {
pub id: String,
pub file_id: Option<Uuid>,
pub bucket_id: Option<Uuid>,
pub action: Option<AccessAction>,
pub user_id: Option<Uuid>,
pub share_id: Option<Uuid>,
pub is_owner: Option<bool>,
pub is_shared: Option<bool>,
pub is_public: Option<bool>,
pub ip_address: Option<String>,
pub user_agent: Option<String>,
pub referer: Option<String>,
pub country_code: Option<String>,
pub city: Option<String>,
pub bytes_transferred: Option<i64>,
pub duration_ms: Option<i32>,
pub success: Option<bool>,
pub error_message: Option<String>,
pub accessed_at: Option<DateTime<Utc>>,
pub metadata: Option<serde_json::Value>,
}
pub struct UpdateAccessLogHandler<R: AccessLogRepository> {
repository: std::sync::Arc<R>,
}
impl<R: AccessLogRepository> UpdateAccessLogHandler<R> {
pub fn new(repository: std::sync::Arc<R>) -> Self {
Self { repository }
}
}
#[async_trait]
impl<R: AccessLogRepository + 'static> CommandHandler<UpdateAccessLogCommand> for UpdateAccessLogHandler<R> {
type Output = Option<AccessLog>;
async fn handle(&self, cmd: UpdateAccessLogCommand) -> Result<Self::Output> {
let existing = self.repository.find_by_id(&cmd.id).await?;
let Some(mut entity) = existing else {
return Ok(None);
};
if let Some(value) = cmd.file_id {
entity.file_id = value;
}
entity.bucket_id = cmd.bucket_id;
if let Some(value) = cmd.action {
entity.action = value;
}
entity.user_id = cmd.user_id;
entity.share_id = cmd.share_id;
if let Some(value) = cmd.is_owner {
entity.is_owner = value;
}
if let Some(value) = cmd.is_shared {
entity.is_shared = value;
}
if let Some(value) = cmd.is_public {
entity.is_public = value;
}
entity.ip_address = cmd.ip_address;
entity.user_agent = cmd.user_agent;
entity.referer = cmd.referer;
entity.country_code = cmd.country_code;
entity.city = cmd.city;
entity.bytes_transferred = cmd.bytes_transferred;
entity.duration_ms = cmd.duration_ms;
if let Some(value) = cmd.success {
entity.success = value;
}
entity.error_message = cmd.error_message;
if let Some(value) = cmd.accessed_at {
entity.accessed_at = value;
}
if let Some(value) = cmd.metadata {
entity.metadata = value;
}
self.repository.update(&cmd.id, &entity).await
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeleteAccessLogCommand {
pub id: String,
pub hard_delete: bool,
}
pub struct DeleteAccessLogHandler<R: AccessLogRepository> {
repository: std::sync::Arc<R>,
}
impl<R: AccessLogRepository> DeleteAccessLogHandler<R> {
pub fn new(repository: std::sync::Arc<R>) -> Self {
Self { repository }
}
}
#[async_trait]
impl<R: AccessLogRepository + 'static> CommandHandler<DeleteAccessLogCommand> for DeleteAccessLogHandler<R> {
type Output = bool;
async fn handle(&self, cmd: DeleteAccessLogCommand) -> Result<Self::Output> {
if cmd.hard_delete {
self.repository.delete(&cmd.id).await
} else {
self.repository.soft_delete(&cmd.id).await
}
}
}