Files
crank/crates/crank-core/src/ext/audit.rs
T
github-ops 13f7a312b8
Deploy / deploy (push) Successful in 2m30s
CI / Rust Checks (push) Successful in 5m30s
CI / UI Checks (push) Successful in 5s
CI / Deployment Manifests (push) Successful in 2s
CI / Frontend E2E (push) Successful in 4m20s
chore: publish clean community baseline
2026-06-19 15:08:22 +00:00

95 lines
2.3 KiB
Rust

use std::sync::Arc;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use time::OffsetDateTime;
use crate::{AuditEventId, UserId, UserSessionId, WorkspaceId};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuditActor {
pub user_id: UserId,
pub email: String,
pub session_id: Option<UserSessionId>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AuditTargetKind {
Workspace,
Membership,
Invitation,
Operation,
Agent,
PlatformApiKey,
Secret,
AuthProfile,
Other,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuditTarget {
pub workspace_id: WorkspaceId,
pub kind: AuditTargetKind,
pub id: String,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct AuditEvent {
pub id: AuditEventId,
#[serde(with = "time::serde::rfc3339")]
pub occurred_at: OffsetDateTime,
pub actor: AuditActor,
pub action: String,
pub target: AuditTarget,
pub payload: Value,
pub source_ip: Option<String>,
pub user_agent: Option<String>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct AuditQuery {
pub workspace_id: Option<WorkspaceId>,
pub action: Option<String>,
pub cursor: Option<String>,
pub limit: Option<u32>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct AuditEventPage {
pub items: Vec<AuditEvent>,
pub next_cursor: Option<String>,
}
#[derive(Debug, thiserror::Error)]
pub enum AuditError {
#[error("audit sink failed: {0}")]
Backend(String),
}
#[async_trait]
pub trait AuditSink: Send + Sync {
async fn record(&self, event: AuditEvent) -> Result<(), AuditError>;
async fn list(&self, _query: AuditQuery) -> Result<AuditEventPage, AuditError> {
Ok(AuditEventPage::default())
}
async fn get(&self, _id: &AuditEventId) -> Result<Option<AuditEvent>, AuditError> {
Ok(None)
}
}
pub type SharedAuditSink = Arc<dyn AuditSink>;
#[derive(Clone, Copy, Debug, Default)]
pub struct NoopAuditSink;
#[async_trait]
impl AuditSink for NoopAuditSink {
async fn record(&self, _event: AuditEvent) -> Result<(), AuditError> {
Ok(())
}
}