1use chrono::{DateTime, Utc};
8use tokio::sync::mpsc;
9
10use crate::types::User;
11
12#[derive(Debug, Clone)]
13#[non_exhaustive]
14pub enum AuthEvent {
15 Registered(RegisteredEvent),
16}
17
18#[derive(Debug, Clone)]
19#[non_exhaustive]
20pub struct RegisteredEvent {
21 pub user: User,
22 pub source: RegistrationSource,
23 pub ctx: EventContext,
24}
25
26impl RegisteredEvent {
27 pub fn new(user: User, source: RegistrationSource, ctx: EventContext) -> Self {
31 Self { user, source, ctx }
32 }
33}
34
35#[derive(Debug, Clone)]
36#[non_exhaustive]
37pub enum RegistrationSource {
38 Password,
39 OAuth { provider: String },
40}
41
42#[derive(Debug, Clone)]
43#[non_exhaustive]
44pub struct EventContext {
45 pub ip: Option<String>,
46 pub user_agent: Option<String>,
47 pub base_url: String,
48 pub occurred_at: DateTime<Utc>,
49}
50
51impl EventContext {
52 pub fn new(
56 ip: Option<String>,
57 user_agent: Option<String>,
58 base_url: String,
59 occurred_at: DateTime<Utc>,
60 ) -> Self {
61 Self {
62 ip,
63 user_agent,
64 base_url,
65 occurred_at,
66 }
67 }
68}
69
70pub type AuthEventSender = mpsc::UnboundedSender<AuthEvent>;
71pub type AuthEventReceiver = mpsc::UnboundedReceiver<AuthEvent>;
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use crate::types::{Email, UserId};
77
78 fn sample_user() -> User {
79 User {
80 id: UserId::new(),
81 email: Email::new("test@example.com".into()).unwrap(),
82 username: None,
83 password_hash: None,
84 email_verified: false,
85 is_active: true,
86 created_at: Utc::now(),
87 updated_at: Utc::now(),
88 custom_data: None,
89 }
90 }
91
92 #[test]
93 fn registered_event_constructs_and_clones() {
94 let event = AuthEvent::Registered(RegisteredEvent::new(
95 sample_user(),
96 RegistrationSource::Password,
97 EventContext::new(
98 Some("127.0.0.1".into()),
99 Some("test-agent".into()),
100 "http://test".into(),
101 Utc::now(),
102 ),
103 ));
104
105 let cloned = event.clone();
106 let _ = format!("{cloned:?}");
108 }
109
110 #[test]
111 fn oauth_source_carries_provider() {
112 let source = RegistrationSource::OAuth {
113 provider: "mock".into(),
114 };
115 let _ = format!("{source:?}");
116 let cloned = source.clone();
117 match cloned {
118 RegistrationSource::OAuth { provider } => assert_eq!(provider, "mock"),
119 _ => panic!("expected OAuth variant"),
120 }
121 }
122}