Initial public release v0.1.0
This commit is contained in:
commit
6c39e0bfbf
87 files changed
+10867
No files matched your search
@@ -0,0 +1,84 @@
|
||||
use crate::{
|
||||
db::{models::AuditSeverity, repository::audit as repo},
|
||||
error::AppError,
|
||||
};
|
||||
|
||||
/// A structured audit event to be persisted and logged.
|
||||
#[derive(Debug)]
|
||||
pub struct AuditEvent<'a> {
|
||||
/// The user performing the action (None for system/CLI events).
|
||||
pub actor_id: Option<&'a str>,
|
||||
/// The user being acted upon, if applicable.
|
||||
pub target_id: Option<&'a str>,
|
||||
/// Machine-readable action name (e.g. `"login_success"`, `"user_created"`).
|
||||
pub action: &'a str,
|
||||
/// Resource category (e.g. `"user"`, `"session"`, `"token"`).
|
||||
pub resource_type: &'a str,
|
||||
/// Specific resource ID, if applicable.
|
||||
pub resource_id: Option<&'a str>,
|
||||
/// Event severity.
|
||||
pub severity: AuditSeverity,
|
||||
/// Client IP address.
|
||||
pub ip: Option<&'a str>,
|
||||
/// Client User-Agent string.
|
||||
pub ua: Option<&'a str>,
|
||||
/// Optional structured metadata (serialized JSON string).
|
||||
pub metadata: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> AuditEvent<'a> {
|
||||
/// Convenience constructor for info-level system events with no actor/IP.
|
||||
pub fn system(action: &'a str, resource_type: &'a str) -> Self {
|
||||
Self {
|
||||
actor_id: None,
|
||||
target_id: None,
|
||||
action,
|
||||
resource_type,
|
||||
resource_id: None,
|
||||
severity: AuditSeverity::Info,
|
||||
ip: None,
|
||||
ua: None,
|
||||
metadata: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Persist an audit event to the database and emit a structured log line.
|
||||
///
|
||||
/// This function is intentionally fire-and-forget — a failure to write an
|
||||
/// audit log must never break an otherwise successful operation.
|
||||
pub async fn log(
|
||||
tx: &mut sqlx::Transaction<'_, sqlx::Sqlite>,
|
||||
event: AuditEvent<'_>,
|
||||
) -> Result<(), AppError> {
|
||||
let id = uuid::Uuid::new_v4().to_string();
|
||||
|
||||
tracing::info!(
|
||||
event = "audit",
|
||||
action = event.action,
|
||||
resource_type = event.resource_type,
|
||||
resource_id = event.resource_id,
|
||||
severity = event.severity.as_str(),
|
||||
actor_id = event.actor_id,
|
||||
target_id = event.target_id,
|
||||
ip = event.ip,
|
||||
);
|
||||
|
||||
repo::insert(
|
||||
tx,
|
||||
&id,
|
||||
event.actor_id,
|
||||
event.target_id,
|
||||
event.action,
|
||||
event.resource_type,
|
||||
event.resource_id,
|
||||
event.severity.as_str(),
|
||||
event.ip,
|
||||
event.ua,
|
||||
event.metadata,
|
||||
)
|
||||
.await
|
||||
.map_err(AppError::Database)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
#[allow(clippy::module_inception)]
|
||||
pub mod audit;
|
||||
pub use audit::{AuditEvent, log};
|
||||
Reference in new issue
Block a user