use vta_sdk::protocols::audit_management::list::AuditLogEntry;
use crate::error::AppError;
use crate::store::KeyspaceHandle;
macro_rules! audit {
($action:expr, actor = $actor:expr, resource = $resource:expr, outcome = $outcome:expr) => {
if $outcome.starts_with("success") {
::tracing::event!(
target: "audit",
::tracing::Level::INFO,
action = $action,
actor = %$actor,
resource = %$resource,
outcome = $outcome,
);
} else {
::tracing::event!(
target: "audit",
::tracing::Level::ERROR,
action = $action,
actor = %$actor,
resource = %$resource,
outcome = $outcome,
);
}
};
($action:expr, actor = $actor:expr, outcome = $outcome:expr) => {
if $outcome.starts_with("success") {
::tracing::event!(
target: "audit",
::tracing::Level::INFO,
action = $action,
actor = %$actor,
outcome = $outcome,
);
} else {
::tracing::event!(
target: "audit",
::tracing::Level::ERROR,
action = $action,
actor = %$actor,
outcome = $outcome,
);
}
};
}
pub(crate) use audit;
pub async fn record(
audit_ks: &KeyspaceHandle,
action: &str,
actor: &str,
resource: Option<&str>,
outcome: &str,
channel: Option<&str>,
context_id: Option<&str>,
) -> Result<(), AppError> {
record_with_detail(
audit_ks, action, actor, resource, outcome, channel, context_id, None,
)
.await
}
#[allow(clippy::too_many_arguments)]
pub async fn record_with_detail(
audit_ks: &KeyspaceHandle,
action: &str,
actor: &str,
resource: Option<&str>,
outcome: &str,
channel: Option<&str>,
context_id: Option<&str>,
detail: Option<&str>,
) -> Result<(), AppError> {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let id = uuid::Uuid::new_v4().to_string();
let entry = AuditLogEntry {
id: id.clone(),
timestamp: now,
action: action.to_string(),
actor: actor.to_string(),
resource: resource.map(String::from),
outcome: outcome.to_string(),
channel: channel.map(String::from),
context_id: context_id.map(String::from),
detail: detail.map(String::from),
};
let key = format!("log:{:020}:{}", now, id);
audit_ks.insert(key, &entry).await
}
pub async fn cleanup_expired_logs(
audit_ks: &KeyspaceHandle,
retention_days: u32,
) -> Result<u64, AppError> {
let cutoff = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
.saturating_sub(retention_days as u64 * 86400);
let cutoff_key = format!("log:{:020}:", cutoff);
let keys = audit_ks.prefix_keys("log:").await?;
let mut removed = 0u64;
for key in keys {
let key_str = String::from_utf8_lossy(&key);
if key_str.as_ref() < cutoff_key.as_str() {
audit_ks.remove(key).await?;
removed += 1;
} else {
break;
}
}
Ok(removed)
}