Files
buzz/crates/buzz-audit/src/action.rs
Will Pfleger d99ad131f1 refactor: rename sprout backend to buzz (#958)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <wpfleger@block.xyz>
Signed-off-by: Will Pfleger <wpfleger@squareup.com>
Signed-off-by: Will Pfleger <wpfleger96@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1fgdl5qqnh3k3f2xkqrvt7cujalhm623x4s7fdjdj5yrtp5fzjl9qrjpucw <4a1bfa0013bc6d14a8d600d8bf6392efefbd2a26ac3c96c9b2a106b0d12297ca@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub16v54tttfqacx9ycvc3k0ut0npj564ahcuajzy6qjvh57ntmsf4uq4806j2 <d32955ad69077062930cc46cfe2df30ca9aaf6f8e76422681265e9e9af704d78@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Will Pfleger <wpfleger96@gmail.com>
2026-06-10 19:29:51 -04:00

101 lines
2.8 KiB
Rust

use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr;
/// Audit action recorded for each event in the log.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AuditAction {
/// A Nostr event was created.
EventCreated,
/// A Nostr event was deleted.
EventDeleted,
/// A channel was created.
ChannelCreated,
/// A channel's metadata was updated.
ChannelUpdated,
/// A channel was deleted.
ChannelDeleted,
/// A member was added to a channel.
MemberAdded,
/// A member was removed from a channel.
MemberRemoved,
/// A client successfully authenticated.
AuthSuccess,
/// A client authentication attempt failed.
AuthFailure,
/// A client exceeded the rate limit.
RateLimitExceeded,
/// A media file was uploaded via the Blossom endpoint.
MediaUploaded,
}
impl AuditAction {
/// Stable string representation used in hash computation and DB storage.
pub fn as_str(&self) -> &'static str {
match self {
Self::EventCreated => "event_created",
Self::EventDeleted => "event_deleted",
Self::ChannelCreated => "channel_created",
Self::ChannelUpdated => "channel_updated",
Self::ChannelDeleted => "channel_deleted",
Self::MemberAdded => "member_added",
Self::MemberRemoved => "member_removed",
Self::AuthSuccess => "auth_success",
Self::AuthFailure => "auth_failure",
Self::RateLimitExceeded => "rate_limit_exceeded",
Self::MediaUploaded => "media_uploaded",
}
}
const ALL: &'static [Self] = &[
Self::EventCreated,
Self::EventDeleted,
Self::ChannelCreated,
Self::ChannelUpdated,
Self::ChannelDeleted,
Self::MemberAdded,
Self::MemberRemoved,
Self::AuthSuccess,
Self::AuthFailure,
Self::RateLimitExceeded,
Self::MediaUploaded,
];
}
impl fmt::Display for AuditAction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl FromStr for AuditAction {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::ALL
.iter()
.find(|a| a.as_str() == s)
.cloned()
.ok_or_else(|| format!("unknown audit action: {s:?}"))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip_all_variants() {
for action in AuditAction::ALL {
let parsed: AuditAction = action.to_string().parse().unwrap();
assert_eq!(&parsed, action);
}
}
#[test]
fn unknown_action_returns_err() {
assert!("totally_bogus".parse::<AuditAction>().is_err());
}
}