events.go raw
1 // Package events provides domain event types and a dispatcher for the ORLY relay.
2 // Domain events represent significant occurrences in the system that other components
3 // may want to react to, enabling loose coupling between components.
4 package events
5
6 import (
7 "time"
8
9 "next.orly.dev/pkg/nostr/encoders/event"
10 )
11
12 // DomainEvent is the base interface for all domain events.
13 type DomainEvent interface {
14 // OccurredAt returns when the event occurred.
15 OccurredAt() time.Time
16 // EventType returns a string identifier for the event type.
17 EventType() string
18 }
19
20 // Base provides common fields for all domain events.
21 type Base struct {
22 occurredAt time.Time
23 eventType string
24 }
25
26 // OccurredAt returns when the event occurred.
27 func (b Base) OccurredAt() time.Time { return b.occurredAt }
28
29 // EventType returns the event type identifier.
30 func (b Base) EventType() string { return b.eventType }
31
32 // newBase creates a new Base with the current time.
33 func newBase(eventType string) Base {
34 return Base{
35 occurredAt: time.Now(),
36 eventType: eventType,
37 }
38 }
39
40 // =============================================================================
41 // Event Storage Events
42 // =============================================================================
43
44 // EventSavedType is the event type for EventSaved.
45 const EventSavedType = "event.saved"
46
47 // EventSaved is emitted when a Nostr event is successfully saved to the database.
48 type EventSaved struct {
49 Base
50 Event *event.E // The saved event
51 Serial uint64 // The assigned serial number
52 IsAdmin bool // Whether the author is an admin
53 IsOwner bool // Whether the author is an owner
54 }
55
56 // NewEventSaved creates a new EventSaved domain event.
57 func NewEventSaved(ev *event.E, serial uint64, isAdmin, isOwner bool) *EventSaved {
58 return &EventSaved{
59 Base: newBase(EventSavedType),
60 Event: ev,
61 Serial: serial,
62 IsAdmin: isAdmin,
63 IsOwner: isOwner,
64 }
65 }
66
67 // EventDeletedType is the event type for EventDeleted.
68 const EventDeletedType = "event.deleted"
69
70 // EventDeleted is emitted when a Nostr event is deleted.
71 type EventDeleted struct {
72 Base
73 EventID []byte // The deleted event ID
74 DeletedBy []byte // Pubkey that requested deletion
75 Serial uint64 // The serial of the deleted event
76 }
77
78 // NewEventDeleted creates a new EventDeleted domain event.
79 func NewEventDeleted(eventID, deletedBy []byte, serial uint64) *EventDeleted {
80 return &EventDeleted{
81 Base: newBase(EventDeletedType),
82 EventID: eventID,
83 DeletedBy: deletedBy,
84 Serial: serial,
85 }
86 }
87
88 // =============================================================================
89 // ACL Events
90 // =============================================================================
91
92 // FollowListUpdatedType is the event type for FollowListUpdated.
93 const FollowListUpdatedType = "acl.followlist.updated"
94
95 // FollowListUpdated is emitted when an admin's follow list changes.
96 type FollowListUpdated struct {
97 Base
98 AdminPubkey []byte // The admin whose follow list changed
99 AddedFollows [][]byte // Pubkeys that were added
100 RemovedFollows [][]byte // Pubkeys that were removed
101 }
102
103 // NewFollowListUpdated creates a new FollowListUpdated domain event.
104 func NewFollowListUpdated(adminPubkey []byte, added, removed [][]byte) *FollowListUpdated {
105 return &FollowListUpdated{
106 Base: newBase(FollowListUpdatedType),
107 AdminPubkey: adminPubkey,
108 AddedFollows: added,
109 RemovedFollows: removed,
110 }
111 }
112
113 // ACLMembershipChangedType is the event type for ACLMembershipChanged.
114 const ACLMembershipChangedType = "acl.membership.changed"
115
116 // ACLMembershipChanged is emitted when a pubkey's access level changes.
117 type ACLMembershipChanged struct {
118 Base
119 Pubkey []byte // The affected pubkey
120 PrevLevel string // Previous access level
121 NewLevel string // New access level
122 Reason string // Reason for the change
123 }
124
125 // NewACLMembershipChanged creates a new ACLMembershipChanged domain event.
126 func NewACLMembershipChanged(pubkey []byte, prevLevel, newLevel, reason string) *ACLMembershipChanged {
127 return &ACLMembershipChanged{
128 Base: newBase(ACLMembershipChangedType),
129 Pubkey: pubkey,
130 PrevLevel: prevLevel,
131 NewLevel: newLevel,
132 Reason: reason,
133 }
134 }
135
136 // =============================================================================
137 // Policy Events
138 // =============================================================================
139
140 // PolicyConfigUpdatedType is the event type for PolicyConfigUpdated.
141 const PolicyConfigUpdatedType = "policy.config.updated"
142
143 // PolicyConfigUpdated is emitted when the policy configuration changes.
144 type PolicyConfigUpdated struct {
145 Base
146 UpdatedBy []byte // Pubkey that made the update
147 Changes map[string]interface{} // Changed configuration keys
148 }
149
150 // NewPolicyConfigUpdated creates a new PolicyConfigUpdated domain event.
151 func NewPolicyConfigUpdated(updatedBy []byte, changes map[string]interface{}) *PolicyConfigUpdated {
152 return &PolicyConfigUpdated{
153 Base: newBase(PolicyConfigUpdatedType),
154 UpdatedBy: updatedBy,
155 Changes: changes,
156 }
157 }
158
159 // PolicyFollowsUpdatedType is the event type for PolicyFollowsUpdated.
160 const PolicyFollowsUpdatedType = "policy.follows.updated"
161
162 // PolicyFollowsUpdated is emitted when policy follow lists are refreshed.
163 type PolicyFollowsUpdated struct {
164 Base
165 AdminPubkey []byte // The admin whose follows were processed
166 FollowCount int // Number of follows in the updated list
167 Follows [][]byte // The follow pubkeys (may be nil for large lists)
168 }
169
170 // NewPolicyFollowsUpdated creates a new PolicyFollowsUpdated domain event.
171 func NewPolicyFollowsUpdated(adminPubkey []byte, followCount int, follows [][]byte) *PolicyFollowsUpdated {
172 return &PolicyFollowsUpdated{
173 Base: newBase(PolicyFollowsUpdatedType),
174 AdminPubkey: adminPubkey,
175 FollowCount: followCount,
176 Follows: follows,
177 }
178 }
179
180 // =============================================================================
181 // Sync Events
182 // =============================================================================
183
184 // RelayGroupConfigChangedType is the event type for RelayGroupConfigChanged.
185 const RelayGroupConfigChangedType = "sync.relaygroup.changed"
186
187 // RelayGroupConfigChanged is emitted when relay group configuration changes.
188 type RelayGroupConfigChanged struct {
189 Base
190 Event *event.E // The kind 39105 event
191 }
192
193 // NewRelayGroupConfigChanged creates a new RelayGroupConfigChanged domain event.
194 func NewRelayGroupConfigChanged(ev *event.E) *RelayGroupConfigChanged {
195 return &RelayGroupConfigChanged{
196 Base: newBase(RelayGroupConfigChangedType),
197 Event: ev,
198 }
199 }
200
201 // ClusterMembershipChangedType is the event type for ClusterMembershipChanged.
202 const ClusterMembershipChangedType = "sync.cluster.membership.changed"
203
204 // ClusterMembershipChanged is emitted when cluster membership changes.
205 type ClusterMembershipChanged struct {
206 Base
207 Event *event.E // The kind 39108 event
208 Action string // "join" or "leave"
209 }
210
211 // NewClusterMembershipChanged creates a new ClusterMembershipChanged domain event.
212 func NewClusterMembershipChanged(ev *event.E, action string) *ClusterMembershipChanged {
213 return &ClusterMembershipChanged{
214 Base: newBase(ClusterMembershipChangedType),
215 Event: ev,
216 Action: action,
217 }
218 }
219
220 // SyncSerialUpdatedType is the event type for SyncSerialUpdated.
221 const SyncSerialUpdatedType = "sync.serial.updated"
222
223 // SyncSerialUpdated is emitted when the sync manager's serial is updated.
224 type SyncSerialUpdated struct {
225 Base
226 Serial uint64 // The new serial number
227 }
228
229 // NewSyncSerialUpdated creates a new SyncSerialUpdated domain event.
230 func NewSyncSerialUpdated(serial uint64) *SyncSerialUpdated {
231 return &SyncSerialUpdated{
232 Base: newBase(SyncSerialUpdatedType),
233 Serial: serial,
234 }
235 }
236
237 // =============================================================================
238 // Authentication Events
239 // =============================================================================
240
241 // UserAuthenticatedType is the event type for UserAuthenticated.
242 const UserAuthenticatedType = "auth.user.authenticated"
243
244 // UserAuthenticated is emitted when a user successfully authenticates.
245 type UserAuthenticated struct {
246 Base
247 Pubkey []byte // The authenticated pubkey
248 AccessLevel string // The granted access level
249 IsFirstTime bool // Whether this is a first-time user
250 ConnectionID string // The connection ID
251 }
252
253 // NewUserAuthenticated creates a new UserAuthenticated domain event.
254 func NewUserAuthenticated(pubkey []byte, accessLevel string, isFirstTime bool, connID string) *UserAuthenticated {
255 return &UserAuthenticated{
256 Base: newBase(UserAuthenticatedType),
257 Pubkey: pubkey,
258 AccessLevel: accessLevel,
259 IsFirstTime: isFirstTime,
260 ConnectionID: connID,
261 }
262 }
263
264 // =============================================================================
265 // Connection Events
266 // =============================================================================
267
268 // ConnectionOpenedType is the event type for ConnectionOpened.
269 const ConnectionOpenedType = "connection.opened"
270
271 // ConnectionOpened is emitted when a new WebSocket connection is established.
272 type ConnectionOpened struct {
273 Base
274 ConnectionID string // Unique connection identifier
275 RemoteAddr string // Client IP:port
276 }
277
278 // NewConnectionOpened creates a new ConnectionOpened domain event.
279 func NewConnectionOpened(connID, remoteAddr string) *ConnectionOpened {
280 return &ConnectionOpened{
281 Base: newBase(ConnectionOpenedType),
282 ConnectionID: connID,
283 RemoteAddr: remoteAddr,
284 }
285 }
286
287 // ConnectionClosedType is the event type for ConnectionClosed.
288 const ConnectionClosedType = "connection.closed"
289
290 // ConnectionClosed is emitted when a WebSocket connection is closed.
291 type ConnectionClosed struct {
292 Base
293 ConnectionID string // Unique connection identifier
294 Duration time.Duration // How long the connection was open
295 EventsReceived int // Number of events received
296 EventsPublished int // Number of events published
297 }
298
299 // NewConnectionClosed creates a new ConnectionClosed domain event.
300 func NewConnectionClosed(connID string, duration time.Duration, received, published int) *ConnectionClosed {
301 return &ConnectionClosed{
302 Base: newBase(ConnectionClosedType),
303 ConnectionID: connID,
304 Duration: duration,
305 EventsReceived: received,
306 EventsPublished: published,
307 }
308 }
309
310 // =============================================================================
311 // Subscription Events
312 // =============================================================================
313
314 // SubscriptionCreatedType is the event type for SubscriptionCreated.
315 const SubscriptionCreatedType = "subscription.created"
316
317 // SubscriptionCreated is emitted when a new REQ subscription is created.
318 type SubscriptionCreated struct {
319 Base
320 SubscriptionID string // The subscription ID from REQ
321 ConnectionID string // The connection this subscription belongs to
322 FilterCount int // Number of filters in the subscription
323 }
324
325 // NewSubscriptionCreated creates a new SubscriptionCreated domain event.
326 func NewSubscriptionCreated(subID, connID string, filterCount int) *SubscriptionCreated {
327 return &SubscriptionCreated{
328 Base: newBase(SubscriptionCreatedType),
329 SubscriptionID: subID,
330 ConnectionID: connID,
331 FilterCount: filterCount,
332 }
333 }
334
335 // SubscriptionClosedType is the event type for SubscriptionClosed.
336 const SubscriptionClosedType = "subscription.closed"
337
338 // SubscriptionClosed is emitted when a subscription is closed.
339 type SubscriptionClosed struct {
340 Base
341 SubscriptionID string // The subscription ID
342 ConnectionID string // The connection this subscription belonged to
343 EventsMatched int // Number of events that matched this subscription
344 }
345
346 // NewSubscriptionClosed creates a new SubscriptionClosed domain event.
347 func NewSubscriptionClosed(subID, connID string, eventsMatched int) *SubscriptionClosed {
348 return &SubscriptionClosed{
349 Base: newBase(SubscriptionClosedType),
350 SubscriptionID: subID,
351 ConnectionID: connID,
352 EventsMatched: eventsMatched,
353 }
354 }
355
356 // =============================================================================
357 // NIP-43 Events
358 // =============================================================================
359
360 // MemberJoinedType is the event type for MemberJoined.
361 const MemberJoinedType = "nip43.member.joined"
362
363 // MemberJoined is emitted when a new member joins via NIP-43.
364 type MemberJoined struct {
365 Base
366 Pubkey []byte // The new member's pubkey
367 InviteCode string // The invite code used (if any)
368 }
369
370 // NewMemberJoined creates a new MemberJoined domain event.
371 func NewMemberJoined(pubkey []byte, inviteCode string) *MemberJoined {
372 return &MemberJoined{
373 Base: newBase(MemberJoinedType),
374 Pubkey: pubkey,
375 InviteCode: inviteCode,
376 }
377 }
378
379 // MemberLeftType is the event type for MemberLeft.
380 const MemberLeftType = "nip43.member.left"
381
382 // MemberLeft is emitted when a member leaves via NIP-43.
383 type MemberLeft struct {
384 Base
385 Pubkey []byte // The departed member's pubkey
386 }
387
388 // NewMemberLeft creates a new MemberLeft domain event.
389 func NewMemberLeft(pubkey []byte) *MemberLeft {
390 return &MemberLeft{
391 Base: newBase(MemberLeftType),
392 Pubkey: pubkey,
393 }
394 }
395
396 // =============================================================================
397 // Event Type Registry
398 // =============================================================================
399
400 // AllEventTypes returns all registered event type constants.
401 func AllEventTypes() []string {
402 return []string{
403 EventSavedType,
404 EventDeletedType,
405 FollowListUpdatedType,
406 ACLMembershipChangedType,
407 PolicyConfigUpdatedType,
408 PolicyFollowsUpdatedType,
409 RelayGroupConfigChangedType,
410 ClusterMembershipChangedType,
411 SyncSerialUpdatedType,
412 UserAuthenticatedType,
413 ConnectionOpenedType,
414 ConnectionClosedType,
415 SubscriptionCreatedType,
416 SubscriptionClosedType,
417 MemberJoinedType,
418 MemberLeftType,
419 }
420 }
421