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