1use std::sync::{Arc, Mutex};
10
11use chrono::{DateTime, Utc};
12use serde::{Deserialize, Serialize};
13use serde_json::{json, Value};
14use uuid::Uuid;
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct AuditEvent {
19 pub id: String,
20 pub timestamp: DateTime<Utc>,
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub workflow_id: Option<String>,
23 pub correlation_id: String,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub actor: Option<String>,
26 pub event_type: String,
27 pub payload: Value,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub trace_id: Option<String>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub run_id: Option<String>,
32}
33
34impl AuditEvent {
35 pub fn new(event_type: impl Into<String>) -> Self {
36 Self {
37 id: format!("evt_{}", Uuid::new_v4().simple()),
38 timestamp: Utc::now(),
39 workflow_id: None,
40 correlation_id: format!("cor_{}", Uuid::new_v4().simple()),
41 actor: None,
42 event_type: event_type.into(),
43 payload: json!({}),
44 trace_id: None,
45 run_id: None,
46 }
47 }
48
49 pub fn with_workflow(mut self, workflow_id: impl Into<String>) -> Self {
50 self.workflow_id = Some(workflow_id.into());
51 self
52 }
53
54 pub fn with_correlation(mut self, correlation_id: impl Into<String>) -> Self {
55 self.correlation_id = correlation_id.into();
56 self
57 }
58
59 pub fn with_actor(mut self, actor: impl Into<String>) -> Self {
60 self.actor = Some(actor.into());
61 self
62 }
63
64 pub fn with_payload(mut self, payload: Value) -> Self {
65 self.payload = payload;
66 self
67 }
68
69 pub fn with_trace_id(mut self, trace_id: impl Into<String>) -> Self {
70 self.trace_id = Some(trace_id.into());
71 self
72 }
73
74 pub fn with_run_id(mut self, run_id: impl Into<String>) -> Self {
75 self.run_id = Some(run_id.into());
76 self
77 }
78}
79
80#[async_trait::async_trait]
82pub trait AuditSink: Send + Sync {
83 async fn record(&self, event: AuditEvent) -> anyhow::Result<()>;
84
85 async fn list_events(&self) -> Option<Vec<AuditEvent>> {
87 None
88 }
89}
90
91pub struct NullAuditSink;
93
94#[async_trait::async_trait]
95impl AuditSink for NullAuditSink {
96 async fn record(&self, _event: AuditEvent) -> anyhow::Result<()> {
97 Ok(())
98 }
99}
100
101pub struct StderrAuditSink;
104
105#[async_trait::async_trait]
106impl AuditSink for StderrAuditSink {
107 async fn record(&self, event: AuditEvent) -> anyhow::Result<()> {
108 eprintln!("{}", serde_json::to_string(&event)?);
109 Ok(())
110 }
111}
112
113#[derive(Default, Clone)]
115pub struct MemoryAuditSink {
116 events: Arc<Mutex<Vec<AuditEvent>>>,
117}
118
119impl MemoryAuditSink {
120 pub fn new() -> Self {
121 Self::default()
122 }
123
124 pub fn snapshot(&self) -> Vec<AuditEvent> {
125 self.events
126 .lock()
127 .expect("LOCK_POISONED: audit event buffer")
128 .clone()
129 }
130
131 pub fn event_types(&self) -> Vec<String> {
132 self.events
133 .lock()
134 .expect("LOCK_POISONED: audit event buffer")
135 .iter()
136 .map(|e| e.event_type.clone())
137 .collect()
138 }
139
140 pub fn clear(&self) {
141 self.events
142 .lock()
143 .expect("LOCK_POISONED: audit event buffer")
144 .clear();
145 }
146}
147
148#[async_trait::async_trait]
149impl AuditSink for MemoryAuditSink {
150 async fn record(&self, event: AuditEvent) -> anyhow::Result<()> {
151 self.events
152 .lock()
153 .expect("LOCK_POISONED: audit event buffer")
154 .push(event);
155 Ok(())
156 }
157
158 async fn list_events(&self) -> Option<Vec<AuditEvent>> {
159 Some(self.snapshot())
160 }
161}