mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-11-02 23:14:06 +01:00
Merge pull request #810 from AnthonyMichaelTDM/eventpayload-interface-improvements
feat(event system): Flesh out EventPayload interface
This commit is contained in:
@@ -10,6 +10,8 @@
|
||||
# jemmy1794 maintains the stream proxy module
|
||||
/src/mod/streamproxy @jemmy1794
|
||||
|
||||
# AnthonyMichaelTDM maintains the plugins module
|
||||
# AnthonyMichaelTDM maintains the plugin and event systems
|
||||
/src/mod/plugins @AnthonyMichaelTDM
|
||||
/example/plugins @AnthonyMichaelTDM
|
||||
/src/**/plugin_*.go @AnthonyMichaelTDM
|
||||
/src/mod/eventsystem @AnthonyMichaelTDM
|
||||
|
||||
@@ -12,12 +12,16 @@ type EventName string
|
||||
type EventPayload interface {
|
||||
// GetName returns the event type
|
||||
GetName() EventName
|
||||
|
||||
// Returns the "source" of the event, that is, the component or plugin that emitted the event
|
||||
GetEventSource() string
|
||||
}
|
||||
|
||||
// Event represents a system event
|
||||
type Event struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"` // Unix timestamp
|
||||
UUID string `json:"uuid"` // UUID for the event
|
||||
Data EventPayload `json:"data"`
|
||||
}
|
||||
|
||||
@@ -28,6 +32,9 @@ const (
|
||||
EventBlacklistToggled EventName = "blacklistToggled"
|
||||
// EventAccessRuleCreated is emitted when a new access ruleset is created
|
||||
EventAccessRuleCreated EventName = "accessRuleCreated"
|
||||
// A custom event emitted by a plugin, with the intention of being broadcast
|
||||
// to the designated recipient(s)
|
||||
EventCustom EventName = "customEvent"
|
||||
|
||||
// Add more event types as needed
|
||||
)
|
||||
@@ -36,6 +43,7 @@ var validEventNames = map[EventName]bool{
|
||||
EventBlacklistedIPBlocked: true,
|
||||
EventBlacklistToggled: true,
|
||||
EventAccessRuleCreated: true,
|
||||
EventCustom: true,
|
||||
// Add more event types as needed
|
||||
// NOTE: Keep up-to-date with event names specified above
|
||||
}
|
||||
@@ -59,6 +67,10 @@ func (e *BlacklistedIPBlockedEvent) GetName() EventName {
|
||||
return EventBlacklistedIPBlocked
|
||||
}
|
||||
|
||||
func (e *BlacklistedIPBlockedEvent) GetEventSource() string {
|
||||
return "proxy-access"
|
||||
}
|
||||
|
||||
// BlacklistToggledEvent represents an event when the blacklist is disabled for an access rule
|
||||
type BlacklistToggledEvent struct {
|
||||
RuleID string `json:"rule_id"`
|
||||
@@ -69,6 +81,10 @@ func (e *BlacklistToggledEvent) GetName() EventName {
|
||||
return EventBlacklistToggled
|
||||
}
|
||||
|
||||
func (e *BlacklistToggledEvent) GetEventSource() string {
|
||||
return "accesslist-api"
|
||||
}
|
||||
|
||||
// AccessRuleCreatedEvent represents an event when a new access ruleset is created
|
||||
type AccessRuleCreatedEvent struct {
|
||||
ID string `json:"id"`
|
||||
@@ -82,12 +98,31 @@ func (e *AccessRuleCreatedEvent) GetName() EventName {
|
||||
return EventAccessRuleCreated
|
||||
}
|
||||
|
||||
func (e *AccessRuleCreatedEvent) GetEventSource() string {
|
||||
return "accesslist-api"
|
||||
}
|
||||
|
||||
type CustomEvent struct {
|
||||
SourcePlugin string `json:"source_plugin"`
|
||||
Recipients []string `json:"recipients"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
}
|
||||
|
||||
func (e *CustomEvent) GetName() EventName {
|
||||
return EventCustom
|
||||
}
|
||||
|
||||
func (e *CustomEvent) GetEventSource() string {
|
||||
return e.SourcePlugin
|
||||
}
|
||||
|
||||
// ParseEvent parses a JSON byte slice into an Event struct
|
||||
func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// First, determine the event type, and parse shared fields, from the JSON data
|
||||
var temp struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
UUID string `json:"uuid"`
|
||||
}
|
||||
if err := json.Unmarshal(jsonData, &temp); err != nil {
|
||||
return err
|
||||
@@ -96,6 +131,7 @@ func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// Set the event name and timestamp
|
||||
event.Name = temp.Name
|
||||
event.Timestamp = temp.Timestamp
|
||||
event.UUID = temp.UUID
|
||||
|
||||
// Now, based on the event type, unmarshal the specific payload
|
||||
switch temp.Name {
|
||||
@@ -126,6 +162,15 @@ func ParseEvent(jsonData []byte, event *Event) error {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
case EventCustom:
|
||||
type tempData struct {
|
||||
Data CustomEvent `json:"data"`
|
||||
}
|
||||
var payload tempData
|
||||
if err := json.Unmarshal(jsonData, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
default:
|
||||
return fmt.Errorf("unknown event: %s, %v", temp.Name, jsonData)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
echo "Copying zoraxy_plugin to all mods"
|
||||
for dir in ./*; do
|
||||
if [ -d "$dir" ]; then
|
||||
# remove existing zoraxy_plugin module, if it exists
|
||||
if [ -d "${dir}/mod/zoraxy_plugin" ]; then
|
||||
rm -r $dir/mod/zoraxy_plugin
|
||||
fi
|
||||
# copy over updated module
|
||||
cp -r ../../src/mod/plugins/zoraxy_plugin "$dir/mod"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -12,12 +12,16 @@ type EventName string
|
||||
type EventPayload interface {
|
||||
// GetName returns the event type
|
||||
GetName() EventName
|
||||
|
||||
// Returns the "source" of the event, that is, the component or plugin that emitted the event
|
||||
GetEventSource() string
|
||||
}
|
||||
|
||||
// Event represents a system event
|
||||
type Event struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"` // Unix timestamp
|
||||
UUID string `json:"uuid"` // UUID for the event
|
||||
Data EventPayload `json:"data"`
|
||||
}
|
||||
|
||||
@@ -28,6 +32,9 @@ const (
|
||||
EventBlacklistToggled EventName = "blacklistToggled"
|
||||
// EventAccessRuleCreated is emitted when a new access ruleset is created
|
||||
EventAccessRuleCreated EventName = "accessRuleCreated"
|
||||
// A custom event emitted by a plugin, with the intention of being broadcast
|
||||
// to the designated recipient(s)
|
||||
EventCustom EventName = "customEvent"
|
||||
|
||||
// Add more event types as needed
|
||||
)
|
||||
@@ -36,6 +43,7 @@ var validEventNames = map[EventName]bool{
|
||||
EventBlacklistedIPBlocked: true,
|
||||
EventBlacklistToggled: true,
|
||||
EventAccessRuleCreated: true,
|
||||
EventCustom: true,
|
||||
// Add more event types as needed
|
||||
// NOTE: Keep up-to-date with event names specified above
|
||||
}
|
||||
@@ -59,6 +67,10 @@ func (e *BlacklistedIPBlockedEvent) GetName() EventName {
|
||||
return EventBlacklistedIPBlocked
|
||||
}
|
||||
|
||||
func (e *BlacklistedIPBlockedEvent) GetEventSource() string {
|
||||
return "proxy-access"
|
||||
}
|
||||
|
||||
// BlacklistToggledEvent represents an event when the blacklist is disabled for an access rule
|
||||
type BlacklistToggledEvent struct {
|
||||
RuleID string `json:"rule_id"`
|
||||
@@ -69,6 +81,10 @@ func (e *BlacklistToggledEvent) GetName() EventName {
|
||||
return EventBlacklistToggled
|
||||
}
|
||||
|
||||
func (e *BlacklistToggledEvent) GetEventSource() string {
|
||||
return "accesslist-api"
|
||||
}
|
||||
|
||||
// AccessRuleCreatedEvent represents an event when a new access ruleset is created
|
||||
type AccessRuleCreatedEvent struct {
|
||||
ID string `json:"id"`
|
||||
@@ -82,12 +98,31 @@ func (e *AccessRuleCreatedEvent) GetName() EventName {
|
||||
return EventAccessRuleCreated
|
||||
}
|
||||
|
||||
func (e *AccessRuleCreatedEvent) GetEventSource() string {
|
||||
return "accesslist-api"
|
||||
}
|
||||
|
||||
type CustomEvent struct {
|
||||
SourcePlugin string `json:"source_plugin"`
|
||||
Recipients []string `json:"recipients"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
}
|
||||
|
||||
func (e *CustomEvent) GetName() EventName {
|
||||
return EventCustom
|
||||
}
|
||||
|
||||
func (e *CustomEvent) GetEventSource() string {
|
||||
return e.SourcePlugin
|
||||
}
|
||||
|
||||
// ParseEvent parses a JSON byte slice into an Event struct
|
||||
func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// First, determine the event type, and parse shared fields, from the JSON data
|
||||
var temp struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
UUID string `json:"uuid"`
|
||||
}
|
||||
if err := json.Unmarshal(jsonData, &temp); err != nil {
|
||||
return err
|
||||
@@ -96,6 +131,7 @@ func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// Set the event name and timestamp
|
||||
event.Name = temp.Name
|
||||
event.Timestamp = temp.Timestamp
|
||||
event.UUID = temp.UUID
|
||||
|
||||
// Now, based on the event type, unmarshal the specific payload
|
||||
switch temp.Name {
|
||||
@@ -126,6 +162,15 @@ func ParseEvent(jsonData []byte, event *Event) error {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
case EventCustom:
|
||||
type tempData struct {
|
||||
Data CustomEvent `json:"data"`
|
||||
}
|
||||
var payload tempData
|
||||
if err := json.Unmarshal(jsonData, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
default:
|
||||
return fmt.Errorf("unknown event: %s, %v", temp.Name, jsonData)
|
||||
}
|
||||
|
||||
@@ -12,12 +12,16 @@ type EventName string
|
||||
type EventPayload interface {
|
||||
// GetName returns the event type
|
||||
GetName() EventName
|
||||
|
||||
// Returns the "source" of the event, that is, the component or plugin that emitted the event
|
||||
GetEventSource() string
|
||||
}
|
||||
|
||||
// Event represents a system event
|
||||
type Event struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"` // Unix timestamp
|
||||
UUID string `json:"uuid"` // UUID for the event
|
||||
Data EventPayload `json:"data"`
|
||||
}
|
||||
|
||||
@@ -28,6 +32,9 @@ const (
|
||||
EventBlacklistToggled EventName = "blacklistToggled"
|
||||
// EventAccessRuleCreated is emitted when a new access ruleset is created
|
||||
EventAccessRuleCreated EventName = "accessRuleCreated"
|
||||
// A custom event emitted by a plugin, with the intention of being broadcast
|
||||
// to the designated recipient(s)
|
||||
EventCustom EventName = "customEvent"
|
||||
|
||||
// Add more event types as needed
|
||||
)
|
||||
@@ -36,6 +43,7 @@ var validEventNames = map[EventName]bool{
|
||||
EventBlacklistedIPBlocked: true,
|
||||
EventBlacklistToggled: true,
|
||||
EventAccessRuleCreated: true,
|
||||
EventCustom: true,
|
||||
// Add more event types as needed
|
||||
// NOTE: Keep up-to-date with event names specified above
|
||||
}
|
||||
@@ -59,6 +67,10 @@ func (e *BlacklistedIPBlockedEvent) GetName() EventName {
|
||||
return EventBlacklistedIPBlocked
|
||||
}
|
||||
|
||||
func (e *BlacklistedIPBlockedEvent) GetEventSource() string {
|
||||
return "proxy-access"
|
||||
}
|
||||
|
||||
// BlacklistToggledEvent represents an event when the blacklist is disabled for an access rule
|
||||
type BlacklistToggledEvent struct {
|
||||
RuleID string `json:"rule_id"`
|
||||
@@ -69,6 +81,10 @@ func (e *BlacklistToggledEvent) GetName() EventName {
|
||||
return EventBlacklistToggled
|
||||
}
|
||||
|
||||
func (e *BlacklistToggledEvent) GetEventSource() string {
|
||||
return "accesslist-api"
|
||||
}
|
||||
|
||||
// AccessRuleCreatedEvent represents an event when a new access ruleset is created
|
||||
type AccessRuleCreatedEvent struct {
|
||||
ID string `json:"id"`
|
||||
@@ -82,12 +98,31 @@ func (e *AccessRuleCreatedEvent) GetName() EventName {
|
||||
return EventAccessRuleCreated
|
||||
}
|
||||
|
||||
func (e *AccessRuleCreatedEvent) GetEventSource() string {
|
||||
return "accesslist-api"
|
||||
}
|
||||
|
||||
type CustomEvent struct {
|
||||
SourcePlugin string `json:"source_plugin"`
|
||||
Recipients []string `json:"recipients"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
}
|
||||
|
||||
func (e *CustomEvent) GetName() EventName {
|
||||
return EventCustom
|
||||
}
|
||||
|
||||
func (e *CustomEvent) GetEventSource() string {
|
||||
return e.SourcePlugin
|
||||
}
|
||||
|
||||
// ParseEvent parses a JSON byte slice into an Event struct
|
||||
func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// First, determine the event type, and parse shared fields, from the JSON data
|
||||
var temp struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
UUID string `json:"uuid"`
|
||||
}
|
||||
if err := json.Unmarshal(jsonData, &temp); err != nil {
|
||||
return err
|
||||
@@ -96,6 +131,7 @@ func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// Set the event name and timestamp
|
||||
event.Name = temp.Name
|
||||
event.Timestamp = temp.Timestamp
|
||||
event.UUID = temp.UUID
|
||||
|
||||
// Now, based on the event type, unmarshal the specific payload
|
||||
switch temp.Name {
|
||||
@@ -126,6 +162,15 @@ func ParseEvent(jsonData []byte, event *Event) error {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
case EventCustom:
|
||||
type tempData struct {
|
||||
Data CustomEvent `json:"data"`
|
||||
}
|
||||
var payload tempData
|
||||
if err := json.Unmarshal(jsonData, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
default:
|
||||
return fmt.Errorf("unknown event: %s, %v", temp.Name, jsonData)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
EventLog = make([]plugin.Event, 0) // A slice to store events
|
||||
EventLog = make([]events.Event, 0) // A slice to store events
|
||||
EventLogMutex = &sync.Mutex{} // Mutex to protect access to the event log
|
||||
)
|
||||
|
||||
@@ -58,7 +58,7 @@ func main() {
|
||||
})
|
||||
http.HandleFunc(EVENT_PATH, func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodPost {
|
||||
var event plugin.Event
|
||||
var event events.Event
|
||||
|
||||
// read the request body
|
||||
if r.Body == nil || r.ContentLength == 0 {
|
||||
@@ -74,7 +74,7 @@ func main() {
|
||||
}
|
||||
|
||||
// parse the event from the request body
|
||||
if err := plugin.ParseEvent(buffer.Bytes(), &event); err != nil {
|
||||
if err := events.ParseEvent(buffer.Bytes(), &event); err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed to parse event: %v", err), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,120 +0,0 @@
|
||||
package zoraxy_plugin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// EventName represents the type of event
|
||||
type EventName string
|
||||
|
||||
// EventPayload interface for all event payloads
|
||||
type EventPayload interface {
|
||||
// GetName returns the event type
|
||||
GetName() EventName
|
||||
}
|
||||
|
||||
// Event represents a system event
|
||||
type Event struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"` // Unix timestamp
|
||||
Data EventPayload `json:"data"`
|
||||
}
|
||||
|
||||
const (
|
||||
// EventBlacklistedIPBlocked is emitted when a blacklisted IP is blocked
|
||||
EventBlacklistedIPBlocked EventName = "blacklistedIpBlocked"
|
||||
// EventBlacklistToggled is emitted when the blacklist is toggled for an access rule
|
||||
EventBlacklistToggled EventName = "blacklistToggled"
|
||||
// EventAccessRuleCreated is emitted when a new access ruleset is created
|
||||
EventAccessRuleCreated EventName = "accessRuleCreated"
|
||||
|
||||
// Add more event types as needed
|
||||
)
|
||||
|
||||
// BlacklistedIPBlockedEvent represents an event when a blacklisted IP is blocked
|
||||
type BlacklistedIPBlockedEvent struct {
|
||||
IP string `json:"ip"`
|
||||
Comment string `json:"comment"`
|
||||
RequestedURL string `json:"requested_url"`
|
||||
Hostname string `json:"hostname"`
|
||||
UserAgent string `json:"user_agent"`
|
||||
Method string `json:"method"`
|
||||
}
|
||||
|
||||
func (e *BlacklistedIPBlockedEvent) GetName() EventName {
|
||||
return EventBlacklistedIPBlocked
|
||||
}
|
||||
|
||||
// BlacklistToggledEvent represents an event when the blacklist is disabled for an access rule
|
||||
type BlacklistToggledEvent struct {
|
||||
RuleID string `json:"rule_id"`
|
||||
Enabled bool `json:"enabled"` // Whether the blacklist is enabled or disabled
|
||||
}
|
||||
|
||||
func (e *BlacklistToggledEvent) GetName() EventName {
|
||||
return EventBlacklistToggled
|
||||
}
|
||||
|
||||
// AccessRuleCreatedEvent represents an event when a new access ruleset is created
|
||||
type AccessRuleCreatedEvent struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Desc string `json:"desc"`
|
||||
BlacklistEnabled bool `json:"blacklist_enabled"`
|
||||
WhitelistEnabled bool `json:"whitelist_enabled"`
|
||||
}
|
||||
|
||||
func (e *AccessRuleCreatedEvent) GetName() EventName {
|
||||
return EventAccessRuleCreated
|
||||
}
|
||||
|
||||
// ParseEvent parses a JSON byte slice into an Event struct
|
||||
func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// First, determine the event type, and parse shared fields, from the JSON data
|
||||
var temp struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
}
|
||||
if err := json.Unmarshal(jsonData, &temp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set the event name and timestamp
|
||||
event.Name = temp.Name
|
||||
event.Timestamp = temp.Timestamp
|
||||
|
||||
// Now, based on the event type, unmarshal the specific payload
|
||||
switch temp.Name {
|
||||
case EventBlacklistedIPBlocked:
|
||||
type tempData struct {
|
||||
Data BlacklistedIPBlockedEvent `json:"data"`
|
||||
}
|
||||
var payload tempData
|
||||
if err := json.Unmarshal(jsonData, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
case EventBlacklistToggled:
|
||||
type tempData struct {
|
||||
Data BlacklistToggledEvent `json:"data"`
|
||||
}
|
||||
var payload tempData
|
||||
if err := json.Unmarshal(jsonData, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
case EventAccessRuleCreated:
|
||||
type tempData struct {
|
||||
Data AccessRuleCreatedEvent `json:"data"`
|
||||
}
|
||||
var payload tempData
|
||||
if err := json.Unmarshal(jsonData, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
default:
|
||||
return fmt.Errorf("unknown event: %s, %v", temp.Name, jsonData)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -12,12 +12,16 @@ type EventName string
|
||||
type EventPayload interface {
|
||||
// GetName returns the event type
|
||||
GetName() EventName
|
||||
|
||||
// Returns the "source" of the event, that is, the component or plugin that emitted the event
|
||||
GetEventSource() string
|
||||
}
|
||||
|
||||
// Event represents a system event
|
||||
type Event struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"` // Unix timestamp
|
||||
UUID string `json:"uuid"` // UUID for the event
|
||||
Data EventPayload `json:"data"`
|
||||
}
|
||||
|
||||
@@ -28,6 +32,9 @@ const (
|
||||
EventBlacklistToggled EventName = "blacklistToggled"
|
||||
// EventAccessRuleCreated is emitted when a new access ruleset is created
|
||||
EventAccessRuleCreated EventName = "accessRuleCreated"
|
||||
// A custom event emitted by a plugin, with the intention of being broadcast
|
||||
// to the designated recipient(s)
|
||||
EventCustom EventName = "customEvent"
|
||||
|
||||
// Add more event types as needed
|
||||
)
|
||||
@@ -36,6 +43,7 @@ var validEventNames = map[EventName]bool{
|
||||
EventBlacklistedIPBlocked: true,
|
||||
EventBlacklistToggled: true,
|
||||
EventAccessRuleCreated: true,
|
||||
EventCustom: true,
|
||||
// Add more event types as needed
|
||||
// NOTE: Keep up-to-date with event names specified above
|
||||
}
|
||||
@@ -59,6 +67,10 @@ func (e *BlacklistedIPBlockedEvent) GetName() EventName {
|
||||
return EventBlacklistedIPBlocked
|
||||
}
|
||||
|
||||
func (e *BlacklistedIPBlockedEvent) GetEventSource() string {
|
||||
return "proxy-access"
|
||||
}
|
||||
|
||||
// BlacklistToggledEvent represents an event when the blacklist is disabled for an access rule
|
||||
type BlacklistToggledEvent struct {
|
||||
RuleID string `json:"rule_id"`
|
||||
@@ -69,6 +81,10 @@ func (e *BlacklistToggledEvent) GetName() EventName {
|
||||
return EventBlacklistToggled
|
||||
}
|
||||
|
||||
func (e *BlacklistToggledEvent) GetEventSource() string {
|
||||
return "accesslist-api"
|
||||
}
|
||||
|
||||
// AccessRuleCreatedEvent represents an event when a new access ruleset is created
|
||||
type AccessRuleCreatedEvent struct {
|
||||
ID string `json:"id"`
|
||||
@@ -82,12 +98,31 @@ func (e *AccessRuleCreatedEvent) GetName() EventName {
|
||||
return EventAccessRuleCreated
|
||||
}
|
||||
|
||||
func (e *AccessRuleCreatedEvent) GetEventSource() string {
|
||||
return "accesslist-api"
|
||||
}
|
||||
|
||||
type CustomEvent struct {
|
||||
SourcePlugin string `json:"source_plugin"`
|
||||
Recipients []string `json:"recipients"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
}
|
||||
|
||||
func (e *CustomEvent) GetName() EventName {
|
||||
return EventCustom
|
||||
}
|
||||
|
||||
func (e *CustomEvent) GetEventSource() string {
|
||||
return e.SourcePlugin
|
||||
}
|
||||
|
||||
// ParseEvent parses a JSON byte slice into an Event struct
|
||||
func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// First, determine the event type, and parse shared fields, from the JSON data
|
||||
var temp struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
UUID string `json:"uuid"`
|
||||
}
|
||||
if err := json.Unmarshal(jsonData, &temp); err != nil {
|
||||
return err
|
||||
@@ -96,6 +131,7 @@ func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// Set the event name and timestamp
|
||||
event.Name = temp.Name
|
||||
event.Timestamp = temp.Timestamp
|
||||
event.UUID = temp.UUID
|
||||
|
||||
// Now, based on the event type, unmarshal the specific payload
|
||||
switch temp.Name {
|
||||
@@ -126,6 +162,15 @@ func ParseEvent(jsonData []byte, event *Event) error {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
case EventCustom:
|
||||
type tempData struct {
|
||||
Data CustomEvent `json:"data"`
|
||||
}
|
||||
var payload tempData
|
||||
if err := json.Unmarshal(jsonData, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
default:
|
||||
return fmt.Errorf("unknown event: %s, %v", temp.Name, jsonData)
|
||||
}
|
||||
|
||||
@@ -12,12 +12,16 @@ type EventName string
|
||||
type EventPayload interface {
|
||||
// GetName returns the event type
|
||||
GetName() EventName
|
||||
|
||||
// Returns the "source" of the event, that is, the component or plugin that emitted the event
|
||||
GetEventSource() string
|
||||
}
|
||||
|
||||
// Event represents a system event
|
||||
type Event struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"` // Unix timestamp
|
||||
UUID string `json:"uuid"` // UUID for the event
|
||||
Data EventPayload `json:"data"`
|
||||
}
|
||||
|
||||
@@ -28,6 +32,9 @@ const (
|
||||
EventBlacklistToggled EventName = "blacklistToggled"
|
||||
// EventAccessRuleCreated is emitted when a new access ruleset is created
|
||||
EventAccessRuleCreated EventName = "accessRuleCreated"
|
||||
// A custom event emitted by a plugin, with the intention of being broadcast
|
||||
// to the designated recipient(s)
|
||||
EventCustom EventName = "customEvent"
|
||||
|
||||
// Add more event types as needed
|
||||
)
|
||||
@@ -36,6 +43,7 @@ var validEventNames = map[EventName]bool{
|
||||
EventBlacklistedIPBlocked: true,
|
||||
EventBlacklistToggled: true,
|
||||
EventAccessRuleCreated: true,
|
||||
EventCustom: true,
|
||||
// Add more event types as needed
|
||||
// NOTE: Keep up-to-date with event names specified above
|
||||
}
|
||||
@@ -59,6 +67,10 @@ func (e *BlacklistedIPBlockedEvent) GetName() EventName {
|
||||
return EventBlacklistedIPBlocked
|
||||
}
|
||||
|
||||
func (e *BlacklistedIPBlockedEvent) GetEventSource() string {
|
||||
return "proxy-access"
|
||||
}
|
||||
|
||||
// BlacklistToggledEvent represents an event when the blacklist is disabled for an access rule
|
||||
type BlacklistToggledEvent struct {
|
||||
RuleID string `json:"rule_id"`
|
||||
@@ -69,6 +81,10 @@ func (e *BlacklistToggledEvent) GetName() EventName {
|
||||
return EventBlacklistToggled
|
||||
}
|
||||
|
||||
func (e *BlacklistToggledEvent) GetEventSource() string {
|
||||
return "accesslist-api"
|
||||
}
|
||||
|
||||
// AccessRuleCreatedEvent represents an event when a new access ruleset is created
|
||||
type AccessRuleCreatedEvent struct {
|
||||
ID string `json:"id"`
|
||||
@@ -82,12 +98,31 @@ func (e *AccessRuleCreatedEvent) GetName() EventName {
|
||||
return EventAccessRuleCreated
|
||||
}
|
||||
|
||||
func (e *AccessRuleCreatedEvent) GetEventSource() string {
|
||||
return "accesslist-api"
|
||||
}
|
||||
|
||||
type CustomEvent struct {
|
||||
SourcePlugin string `json:"source_plugin"`
|
||||
Recipients []string `json:"recipients"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
}
|
||||
|
||||
func (e *CustomEvent) GetName() EventName {
|
||||
return EventCustom
|
||||
}
|
||||
|
||||
func (e *CustomEvent) GetEventSource() string {
|
||||
return e.SourcePlugin
|
||||
}
|
||||
|
||||
// ParseEvent parses a JSON byte slice into an Event struct
|
||||
func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// First, determine the event type, and parse shared fields, from the JSON data
|
||||
var temp struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
UUID string `json:"uuid"`
|
||||
}
|
||||
if err := json.Unmarshal(jsonData, &temp); err != nil {
|
||||
return err
|
||||
@@ -96,6 +131,7 @@ func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// Set the event name and timestamp
|
||||
event.Name = temp.Name
|
||||
event.Timestamp = temp.Timestamp
|
||||
event.UUID = temp.UUID
|
||||
|
||||
// Now, based on the event type, unmarshal the specific payload
|
||||
switch temp.Name {
|
||||
@@ -126,6 +162,15 @@ func ParseEvent(jsonData []byte, event *Event) error {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
case EventCustom:
|
||||
type tempData struct {
|
||||
Data CustomEvent `json:"data"`
|
||||
}
|
||||
var payload tempData
|
||||
if err := json.Unmarshal(jsonData, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
default:
|
||||
return fmt.Errorf("unknown event: %s, %v", temp.Name, jsonData)
|
||||
}
|
||||
|
||||
@@ -12,12 +12,16 @@ type EventName string
|
||||
type EventPayload interface {
|
||||
// GetName returns the event type
|
||||
GetName() EventName
|
||||
|
||||
// Returns the "source" of the event, that is, the component or plugin that emitted the event
|
||||
GetEventSource() string
|
||||
}
|
||||
|
||||
// Event represents a system event
|
||||
type Event struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"` // Unix timestamp
|
||||
UUID string `json:"uuid"` // UUID for the event
|
||||
Data EventPayload `json:"data"`
|
||||
}
|
||||
|
||||
@@ -28,6 +32,9 @@ const (
|
||||
EventBlacklistToggled EventName = "blacklistToggled"
|
||||
// EventAccessRuleCreated is emitted when a new access ruleset is created
|
||||
EventAccessRuleCreated EventName = "accessRuleCreated"
|
||||
// A custom event emitted by a plugin, with the intention of being broadcast
|
||||
// to the designated recipient(s)
|
||||
EventCustom EventName = "customEvent"
|
||||
|
||||
// Add more event types as needed
|
||||
)
|
||||
@@ -36,6 +43,7 @@ var validEventNames = map[EventName]bool{
|
||||
EventBlacklistedIPBlocked: true,
|
||||
EventBlacklistToggled: true,
|
||||
EventAccessRuleCreated: true,
|
||||
EventCustom: true,
|
||||
// Add more event types as needed
|
||||
// NOTE: Keep up-to-date with event names specified above
|
||||
}
|
||||
@@ -59,6 +67,10 @@ func (e *BlacklistedIPBlockedEvent) GetName() EventName {
|
||||
return EventBlacklistedIPBlocked
|
||||
}
|
||||
|
||||
func (e *BlacklistedIPBlockedEvent) GetEventSource() string {
|
||||
return "proxy-access"
|
||||
}
|
||||
|
||||
// BlacklistToggledEvent represents an event when the blacklist is disabled for an access rule
|
||||
type BlacklistToggledEvent struct {
|
||||
RuleID string `json:"rule_id"`
|
||||
@@ -69,6 +81,10 @@ func (e *BlacklistToggledEvent) GetName() EventName {
|
||||
return EventBlacklistToggled
|
||||
}
|
||||
|
||||
func (e *BlacklistToggledEvent) GetEventSource() string {
|
||||
return "accesslist-api"
|
||||
}
|
||||
|
||||
// AccessRuleCreatedEvent represents an event when a new access ruleset is created
|
||||
type AccessRuleCreatedEvent struct {
|
||||
ID string `json:"id"`
|
||||
@@ -82,12 +98,31 @@ func (e *AccessRuleCreatedEvent) GetName() EventName {
|
||||
return EventAccessRuleCreated
|
||||
}
|
||||
|
||||
func (e *AccessRuleCreatedEvent) GetEventSource() string {
|
||||
return "accesslist-api"
|
||||
}
|
||||
|
||||
type CustomEvent struct {
|
||||
SourcePlugin string `json:"source_plugin"`
|
||||
Recipients []string `json:"recipients"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
}
|
||||
|
||||
func (e *CustomEvent) GetName() EventName {
|
||||
return EventCustom
|
||||
}
|
||||
|
||||
func (e *CustomEvent) GetEventSource() string {
|
||||
return e.SourcePlugin
|
||||
}
|
||||
|
||||
// ParseEvent parses a JSON byte slice into an Event struct
|
||||
func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// First, determine the event type, and parse shared fields, from the JSON data
|
||||
var temp struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
UUID string `json:"uuid"`
|
||||
}
|
||||
if err := json.Unmarshal(jsonData, &temp); err != nil {
|
||||
return err
|
||||
@@ -96,6 +131,7 @@ func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// Set the event name and timestamp
|
||||
event.Name = temp.Name
|
||||
event.Timestamp = temp.Timestamp
|
||||
event.UUID = temp.UUID
|
||||
|
||||
// Now, based on the event type, unmarshal the specific payload
|
||||
switch temp.Name {
|
||||
@@ -126,6 +162,15 @@ func ParseEvent(jsonData []byte, event *Event) error {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
case EventCustom:
|
||||
type tempData struct {
|
||||
Data CustomEvent `json:"data"`
|
||||
}
|
||||
var payload tempData
|
||||
if err := json.Unmarshal(jsonData, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
default:
|
||||
return fmt.Errorf("unknown event: %s, %v", temp.Name, jsonData)
|
||||
}
|
||||
|
||||
@@ -12,12 +12,16 @@ type EventName string
|
||||
type EventPayload interface {
|
||||
// GetName returns the event type
|
||||
GetName() EventName
|
||||
|
||||
// Returns the "source" of the event, that is, the component or plugin that emitted the event
|
||||
GetEventSource() string
|
||||
}
|
||||
|
||||
// Event represents a system event
|
||||
type Event struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"` // Unix timestamp
|
||||
UUID string `json:"uuid"` // UUID for the event
|
||||
Data EventPayload `json:"data"`
|
||||
}
|
||||
|
||||
@@ -28,6 +32,9 @@ const (
|
||||
EventBlacklistToggled EventName = "blacklistToggled"
|
||||
// EventAccessRuleCreated is emitted when a new access ruleset is created
|
||||
EventAccessRuleCreated EventName = "accessRuleCreated"
|
||||
// A custom event emitted by a plugin, with the intention of being broadcast
|
||||
// to the designated recipient(s)
|
||||
EventCustom EventName = "customEvent"
|
||||
|
||||
// Add more event types as needed
|
||||
)
|
||||
@@ -36,6 +43,7 @@ var validEventNames = map[EventName]bool{
|
||||
EventBlacklistedIPBlocked: true,
|
||||
EventBlacklistToggled: true,
|
||||
EventAccessRuleCreated: true,
|
||||
EventCustom: true,
|
||||
// Add more event types as needed
|
||||
// NOTE: Keep up-to-date with event names specified above
|
||||
}
|
||||
@@ -59,6 +67,10 @@ func (e *BlacklistedIPBlockedEvent) GetName() EventName {
|
||||
return EventBlacklistedIPBlocked
|
||||
}
|
||||
|
||||
func (e *BlacklistedIPBlockedEvent) GetEventSource() string {
|
||||
return "proxy-access"
|
||||
}
|
||||
|
||||
// BlacklistToggledEvent represents an event when the blacklist is disabled for an access rule
|
||||
type BlacklistToggledEvent struct {
|
||||
RuleID string `json:"rule_id"`
|
||||
@@ -69,6 +81,10 @@ func (e *BlacklistToggledEvent) GetName() EventName {
|
||||
return EventBlacklistToggled
|
||||
}
|
||||
|
||||
func (e *BlacklistToggledEvent) GetEventSource() string {
|
||||
return "accesslist-api"
|
||||
}
|
||||
|
||||
// AccessRuleCreatedEvent represents an event when a new access ruleset is created
|
||||
type AccessRuleCreatedEvent struct {
|
||||
ID string `json:"id"`
|
||||
@@ -82,12 +98,31 @@ func (e *AccessRuleCreatedEvent) GetName() EventName {
|
||||
return EventAccessRuleCreated
|
||||
}
|
||||
|
||||
func (e *AccessRuleCreatedEvent) GetEventSource() string {
|
||||
return "accesslist-api"
|
||||
}
|
||||
|
||||
type CustomEvent struct {
|
||||
SourcePlugin string `json:"source_plugin"`
|
||||
Recipients []string `json:"recipients"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
}
|
||||
|
||||
func (e *CustomEvent) GetName() EventName {
|
||||
return EventCustom
|
||||
}
|
||||
|
||||
func (e *CustomEvent) GetEventSource() string {
|
||||
return e.SourcePlugin
|
||||
}
|
||||
|
||||
// ParseEvent parses a JSON byte slice into an Event struct
|
||||
func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// First, determine the event type, and parse shared fields, from the JSON data
|
||||
var temp struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
UUID string `json:"uuid"`
|
||||
}
|
||||
if err := json.Unmarshal(jsonData, &temp); err != nil {
|
||||
return err
|
||||
@@ -96,6 +131,7 @@ func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// Set the event name and timestamp
|
||||
event.Name = temp.Name
|
||||
event.Timestamp = temp.Timestamp
|
||||
event.UUID = temp.UUID
|
||||
|
||||
// Now, based on the event type, unmarshal the specific payload
|
||||
switch temp.Name {
|
||||
@@ -126,6 +162,15 @@ func ParseEvent(jsonData []byte, event *Event) error {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
case EventCustom:
|
||||
type tempData struct {
|
||||
Data CustomEvent `json:"data"`
|
||||
}
|
||||
var payload tempData
|
||||
if err := json.Unmarshal(jsonData, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
default:
|
||||
return fmt.Errorf("unknown event: %s, %v", temp.Name, jsonData)
|
||||
}
|
||||
|
||||
@@ -12,12 +12,16 @@ type EventName string
|
||||
type EventPayload interface {
|
||||
// GetName returns the event type
|
||||
GetName() EventName
|
||||
|
||||
// Returns the "source" of the event, that is, the component or plugin that emitted the event
|
||||
GetEventSource() string
|
||||
}
|
||||
|
||||
// Event represents a system event
|
||||
type Event struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"` // Unix timestamp
|
||||
UUID string `json:"uuid"` // UUID for the event
|
||||
Data EventPayload `json:"data"`
|
||||
}
|
||||
|
||||
@@ -28,6 +32,9 @@ const (
|
||||
EventBlacklistToggled EventName = "blacklistToggled"
|
||||
// EventAccessRuleCreated is emitted when a new access ruleset is created
|
||||
EventAccessRuleCreated EventName = "accessRuleCreated"
|
||||
// A custom event emitted by a plugin, with the intention of being broadcast
|
||||
// to the designated recipient(s)
|
||||
EventCustom EventName = "customEvent"
|
||||
|
||||
// Add more event types as needed
|
||||
)
|
||||
@@ -36,6 +43,7 @@ var validEventNames = map[EventName]bool{
|
||||
EventBlacklistedIPBlocked: true,
|
||||
EventBlacklistToggled: true,
|
||||
EventAccessRuleCreated: true,
|
||||
EventCustom: true,
|
||||
// Add more event types as needed
|
||||
// NOTE: Keep up-to-date with event names specified above
|
||||
}
|
||||
@@ -59,6 +67,10 @@ func (e *BlacklistedIPBlockedEvent) GetName() EventName {
|
||||
return EventBlacklistedIPBlocked
|
||||
}
|
||||
|
||||
func (e *BlacklistedIPBlockedEvent) GetEventSource() string {
|
||||
return "proxy-access"
|
||||
}
|
||||
|
||||
// BlacklistToggledEvent represents an event when the blacklist is disabled for an access rule
|
||||
type BlacklistToggledEvent struct {
|
||||
RuleID string `json:"rule_id"`
|
||||
@@ -69,6 +81,10 @@ func (e *BlacklistToggledEvent) GetName() EventName {
|
||||
return EventBlacklistToggled
|
||||
}
|
||||
|
||||
func (e *BlacklistToggledEvent) GetEventSource() string {
|
||||
return "accesslist-api"
|
||||
}
|
||||
|
||||
// AccessRuleCreatedEvent represents an event when a new access ruleset is created
|
||||
type AccessRuleCreatedEvent struct {
|
||||
ID string `json:"id"`
|
||||
@@ -82,12 +98,31 @@ func (e *AccessRuleCreatedEvent) GetName() EventName {
|
||||
return EventAccessRuleCreated
|
||||
}
|
||||
|
||||
func (e *AccessRuleCreatedEvent) GetEventSource() string {
|
||||
return "accesslist-api"
|
||||
}
|
||||
|
||||
type CustomEvent struct {
|
||||
SourcePlugin string `json:"source_plugin"`
|
||||
Recipients []string `json:"recipients"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
}
|
||||
|
||||
func (e *CustomEvent) GetName() EventName {
|
||||
return EventCustom
|
||||
}
|
||||
|
||||
func (e *CustomEvent) GetEventSource() string {
|
||||
return e.SourcePlugin
|
||||
}
|
||||
|
||||
// ParseEvent parses a JSON byte slice into an Event struct
|
||||
func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// First, determine the event type, and parse shared fields, from the JSON data
|
||||
var temp struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
UUID string `json:"uuid"`
|
||||
}
|
||||
if err := json.Unmarshal(jsonData, &temp); err != nil {
|
||||
return err
|
||||
@@ -96,6 +131,7 @@ func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// Set the event name and timestamp
|
||||
event.Name = temp.Name
|
||||
event.Timestamp = temp.Timestamp
|
||||
event.UUID = temp.UUID
|
||||
|
||||
// Now, based on the event type, unmarshal the specific payload
|
||||
switch temp.Name {
|
||||
@@ -126,6 +162,15 @@ func ParseEvent(jsonData []byte, event *Event) error {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
case EventCustom:
|
||||
type tempData struct {
|
||||
Data CustomEvent `json:"data"`
|
||||
}
|
||||
var payload tempData
|
||||
if err := json.Unmarshal(jsonData, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
default:
|
||||
return fmt.Errorf("unknown event: %s, %v", temp.Name, jsonData)
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"imuslab.com/zoraxy/mod/info/logger"
|
||||
// "imuslab.com/zoraxy/mod/plugins"
|
||||
|
||||
"imuslab.com/zoraxy/mod/plugins/zoraxy_plugin/events"
|
||||
)
|
||||
@@ -86,8 +86,39 @@ func (em *eventManager) UnregisterSubscriber(listenerID ListenerID) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// EmitToSubscribersAnd dispatches an event to the specific listeners in addition to the events subscribers.
|
||||
//
|
||||
// The primary use-case of this function is for plugin-to-plugin communication
|
||||
func (em *eventManager) EmitToSubscribersAnd(listenerIDs []ListenerID, payload events.EventPayload) {
|
||||
eventName := payload.GetName()
|
||||
|
||||
if len(listenerIDs) == 0 {
|
||||
return // No listeners specified
|
||||
}
|
||||
|
||||
// Create the event
|
||||
event := events.Event{
|
||||
Name: eventName,
|
||||
Timestamp: time.Now().Unix(),
|
||||
UUID: uuid.New().String(),
|
||||
Data: payload,
|
||||
}
|
||||
|
||||
// Dispatch to all specified listeners asynchronously
|
||||
em.emitTo(listenerIDs, event)
|
||||
|
||||
// Also emit to all subscribers of the event as usual
|
||||
em.mutex.RLock()
|
||||
defer em.mutex.RUnlock()
|
||||
subscribers, exists := em.subscriptions[eventName]
|
||||
if !exists || len(subscribers) == 0 {
|
||||
return // No subscribers
|
||||
}
|
||||
em.emitTo(subscribers, event)
|
||||
}
|
||||
|
||||
// Emit dispatches an event to all subscribed listeners
|
||||
func (em *eventManager) Emit(payload events.EventPayload) error {
|
||||
func (em *eventManager) Emit(payload events.EventPayload) {
|
||||
eventName := payload.GetName()
|
||||
|
||||
em.mutex.RLock()
|
||||
@@ -95,22 +126,38 @@ func (em *eventManager) Emit(payload events.EventPayload) error {
|
||||
subscribers, exists := em.subscriptions[eventName]
|
||||
|
||||
if !exists || len(subscribers) == 0 {
|
||||
return nil // No subscribers
|
||||
return // No subscribers
|
||||
}
|
||||
|
||||
// Create the event
|
||||
event := events.Event{
|
||||
Name: eventName,
|
||||
Timestamp: time.Now().Unix(),
|
||||
UUID: uuid.New().String(),
|
||||
Data: payload,
|
||||
}
|
||||
|
||||
// Dispatch to all subscribers asynchronously
|
||||
for _, listenerID := range subscribers {
|
||||
em.emitTo(subscribers, event)
|
||||
}
|
||||
|
||||
// Dispatch event to all specified listeners asynchronously
|
||||
func (em *eventManager) emitTo(listenerIDs []ListenerID, event events.Event) {
|
||||
if len(listenerIDs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Dispatch to all specified listeners asynchronously
|
||||
em.mutex.RLock()
|
||||
defer em.mutex.RUnlock()
|
||||
listenersToUnregister := []ListenerID{}
|
||||
for _, listenerID := range listenerIDs {
|
||||
listener, exists := em.subscribers[listenerID]
|
||||
|
||||
if !exists {
|
||||
em.logger.PrintAndLog("event-system", "Failed to get listener for event dispatch, removing "+string(listenerID)+" from subscriptions", nil)
|
||||
// Mark for removal
|
||||
listenersToUnregister = append(listenersToUnregister, listenerID)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -121,5 +168,10 @@ func (em *eventManager) Emit(payload events.EventPayload) error {
|
||||
}(listener)
|
||||
}
|
||||
|
||||
return nil
|
||||
// Unregister any listeners that no longer exist, asynchronously
|
||||
go func() {
|
||||
for _, id := range listenersToUnregister {
|
||||
em.UnregisterSubscriber(id)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"imuslab.com/zoraxy/mod/info/logger"
|
||||
"imuslab.com/zoraxy/mod/plugins/zoraxy_plugin/events"
|
||||
)
|
||||
|
||||
@@ -18,6 +20,7 @@ func TestEventDeSerialization(t *testing.T) {
|
||||
}
|
||||
|
||||
timestamp := time.Now().Unix()
|
||||
uuid := uuid.New().String()
|
||||
|
||||
tests := []SerializationTest{
|
||||
{
|
||||
@@ -25,6 +28,7 @@ func TestEventDeSerialization(t *testing.T) {
|
||||
event: events.Event{
|
||||
Name: events.EventBlacklistedIPBlocked,
|
||||
Timestamp: timestamp,
|
||||
UUID: uuid,
|
||||
Data: &events.BlacklistedIPBlockedEvent{
|
||||
IP: "192.168.1.1",
|
||||
Comment: "Test comment",
|
||||
@@ -34,25 +38,27 @@ func TestEventDeSerialization(t *testing.T) {
|
||||
Method: "GET",
|
||||
},
|
||||
},
|
||||
expectedJson: `{"name":"blacklistedIpBlocked","timestamp":` + fmt.Sprintf("%d", timestamp) + `,"data":{"ip":"192.168.1.1","comment":"Test comment","requested_url":"http://example.com","hostname":"example.com","user_agent":"TestUserAgent","method":"GET"}}`,
|
||||
expectedJson: `{"name":"blacklistedIpBlocked","timestamp":` + fmt.Sprintf("%d", timestamp) + `,"uuid":"` + uuid + `","data":{"ip":"192.168.1.1","comment":"Test comment","requested_url":"http://example.com","hostname":"example.com","user_agent":"TestUserAgent","method":"GET"}}`,
|
||||
},
|
||||
{
|
||||
name: "BlacklistToggled",
|
||||
event: events.Event{
|
||||
Name: events.EventBlacklistToggled,
|
||||
Timestamp: timestamp,
|
||||
UUID: uuid,
|
||||
Data: &events.BlacklistToggledEvent{
|
||||
RuleID: "rule123",
|
||||
Enabled: true,
|
||||
},
|
||||
},
|
||||
expectedJson: `{"name":"blacklistToggled","timestamp":` + fmt.Sprintf("%d", timestamp) + `,"data":{"rule_id":"rule123","enabled":true}}`,
|
||||
expectedJson: `{"name":"blacklistToggled","timestamp":` + fmt.Sprintf("%d", timestamp) + `,"uuid":"` + uuid + `","data":{"rule_id":"rule123","enabled":true}}`,
|
||||
},
|
||||
{
|
||||
name: "AccessRuleCreated",
|
||||
event: events.Event{
|
||||
Name: events.EventAccessRuleCreated,
|
||||
Timestamp: timestamp,
|
||||
UUID: uuid,
|
||||
Data: &events.AccessRuleCreatedEvent{
|
||||
ID: "rule456",
|
||||
Name: "New Access Rule",
|
||||
@@ -61,7 +67,7 @@ func TestEventDeSerialization(t *testing.T) {
|
||||
WhitelistEnabled: false,
|
||||
},
|
||||
},
|
||||
expectedJson: `{"name":"accessRuleCreated","timestamp":` + fmt.Sprintf("%d", timestamp) + `,"data":{"id":"rule456","name":"New Access Rule","desc":"A dummy access rule","blacklist_enabled":true,"whitelist_enabled":false}}`,
|
||||
expectedJson: `{"name":"accessRuleCreated","timestamp":` + fmt.Sprintf("%d", timestamp) + `,"uuid":"` + uuid + `","data":{"id":"rule456","name":"New Access Rule","desc":"A dummy access rule","blacklist_enabled":true,"whitelist_enabled":false}}`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -111,3 +117,271 @@ func TestEventDeSerialization(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type TestListener struct {
|
||||
id ListenerID
|
||||
receivedEvents chan events.Event
|
||||
}
|
||||
|
||||
func (tl *TestListener) GetID() ListenerID {
|
||||
return tl.id
|
||||
}
|
||||
|
||||
func (tl *TestListener) Notify(event events.Event) error {
|
||||
tl.receivedEvents <- event
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestEventEmission(t *testing.T) {
|
||||
logger, err := logger.NewFmtLogger()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create logger: %v", err)
|
||||
}
|
||||
em := eventManager{
|
||||
subscriptions: make(map[events.EventName][]ListenerID),
|
||||
subscribers: make(map[ListenerID]Listener),
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
// Create a test listener
|
||||
listenerID := ListenerID("testListener")
|
||||
testListener := &TestListener{
|
||||
id: listenerID,
|
||||
receivedEvents: make(chan events.Event, 10),
|
||||
}
|
||||
|
||||
// Register the listener for BlacklistedIPBlocked events
|
||||
if err := em.RegisterSubscriberToEvent(testListener, events.EventBlacklistedIPBlocked); err != nil {
|
||||
t.Fatalf("Failed to register subscriber: %v", err)
|
||||
}
|
||||
|
||||
// Emit a BlacklistedIPBlocked event
|
||||
testEvent := &events.BlacklistedIPBlockedEvent{
|
||||
IP: "192.168.1.1",
|
||||
Comment: "Malicious activity detected",
|
||||
RequestedURL: "http://mysite.com/admin",
|
||||
Hostname: "mysite.com",
|
||||
UserAgent: "BadBot/1.0",
|
||||
Method: "GET",
|
||||
}
|
||||
em.Emit(testEvent)
|
||||
|
||||
// Verify that the listener received the event
|
||||
select {
|
||||
case receivedEvent := <-testListener.receivedEvents:
|
||||
if receivedEvent.Name != events.EventBlacklistedIPBlocked {
|
||||
t.Fatalf("Unexpected event received by listener.\nGot: %+v\nWant: %+v", receivedEvent, testEvent)
|
||||
}
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("Timeout waiting for event to be received by listener")
|
||||
}
|
||||
// Unregister the listener
|
||||
if err := em.UnregisterSubscriber(listenerID); err != nil {
|
||||
t.Fatalf("Failed to unregister subscriber: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventEmissionToSpecificListener(t *testing.T) {
|
||||
logger, err := logger.NewFmtLogger()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create logger: %v", err)
|
||||
}
|
||||
em := eventManager{
|
||||
subscriptions: make(map[events.EventName][]ListenerID),
|
||||
subscribers: make(map[ListenerID]Listener),
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
// Create a few test listeners
|
||||
// listener1 and listener2 are plugins that will send events to each other
|
||||
// moderator is a plugin that is subscribed to EventCustom, so it will receive all custom events
|
||||
// otherListener is neither subscribed to EventCustom nor a recipient of any custom event, so it should not receive any events
|
||||
listener1ID := ListenerID("pluginA")
|
||||
listener1 := &TestListener{
|
||||
id: listener1ID,
|
||||
receivedEvents: make(chan events.Event, 10),
|
||||
}
|
||||
em.RegisterSubscriberToEvent(listener1, events.EventAccessRuleCreated) // We need to register to some event to be a valid subscriber
|
||||
|
||||
listener2ID := ListenerID("pluginB")
|
||||
listener2 := &TestListener{
|
||||
id: listener2ID,
|
||||
receivedEvents: make(chan events.Event, 10),
|
||||
}
|
||||
em.RegisterSubscriberToEvent(listener2, events.EventAccessRuleCreated) // We need to register to some event to be a valid subscriber
|
||||
|
||||
moderatorID := ListenerID("moderator")
|
||||
moderator := &TestListener{
|
||||
id: moderatorID,
|
||||
receivedEvents: make(chan events.Event, 10),
|
||||
}
|
||||
em.RegisterSubscriberToEvent(moderator, events.EventCustom)
|
||||
|
||||
otherListenerID := ListenerID("pluginD")
|
||||
otherListener := &TestListener{
|
||||
id: otherListenerID,
|
||||
receivedEvents: make(chan events.Event, 10),
|
||||
}
|
||||
em.RegisterSubscriberToEvent(otherListener, events.EventAccessRuleCreated) // We need to register to some event to be a valid subscriber
|
||||
|
||||
// Send a custom event from listener1 to listener2
|
||||
customEvent := &events.CustomEvent{
|
||||
SourcePlugin: "pluginA",
|
||||
Recipients: []string{"pluginB"},
|
||||
Payload: map[string]interface{}{
|
||||
"message": "Hello from pluginA",
|
||||
},
|
||||
}
|
||||
em.EmitToSubscribersAnd([]ListenerID{listener2ID}, customEvent)
|
||||
|
||||
// Verify that listener2 received the event
|
||||
select {
|
||||
case receivedEvent := <-listener2.receivedEvents:
|
||||
if receivedEvent.Name != events.EventCustom {
|
||||
t.Fatalf("Unexpected event received by listener2.\nGot: %+v\nWant: %+v", receivedEvent, customEvent)
|
||||
}
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("Timeout waiting for event to be received by listener2")
|
||||
}
|
||||
// Verify that listener1 did not receive the event
|
||||
select {
|
||||
case <-listener1.receivedEvents:
|
||||
t.Fatal("Listener1 should not have received any events")
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
// No event received, as expected
|
||||
}
|
||||
|
||||
// send a custom event from listener2 to listener1
|
||||
customEvent2 := &events.CustomEvent{
|
||||
SourcePlugin: "pluginB",
|
||||
Recipients: []string{"pluginA"},
|
||||
Payload: map[string]interface{}{
|
||||
"message": "Hello from pluginB",
|
||||
},
|
||||
}
|
||||
em.EmitToSubscribersAnd([]ListenerID{listener1ID}, customEvent2)
|
||||
|
||||
// Verify that listener1 received the event
|
||||
select {
|
||||
case receivedEvent := <-listener1.receivedEvents:
|
||||
if receivedEvent.Name != events.EventCustom {
|
||||
t.Fatalf("Unexpected event received by listener1.\nGot: %+v\nWant: %+v", receivedEvent, customEvent2)
|
||||
}
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("Timeout waiting for event to be received by listener1")
|
||||
}
|
||||
// Verify that listener2 did not receive any new event
|
||||
select {
|
||||
case <-listener2.receivedEvents:
|
||||
t.Fatal("Listener2 should not have received any new events")
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
// No event received, as expected
|
||||
}
|
||||
|
||||
// ensure the moderator received both events
|
||||
expectedMessagesSeen := map[string]bool{
|
||||
"Hello from pluginA": false,
|
||||
"Hello from pluginB": false,
|
||||
}
|
||||
for range expectedMessagesSeen {
|
||||
select {
|
||||
case receivedEvent := <-moderator.receivedEvents:
|
||||
if receivedEvent.Name != events.EventCustom {
|
||||
t.Fatalf("Unexpected event received by moderator.\nGot: %+v\nWant: %+v", receivedEvent, customEvent)
|
||||
}
|
||||
data, ok := receivedEvent.Data.(*events.CustomEvent)
|
||||
if !ok {
|
||||
t.Fatalf("Unexpected data type in event received by moderator.\nGot: %T\nWant: *events.CustomEvent", receivedEvent.Data)
|
||||
}
|
||||
message, ok := data.Payload["message"].(string)
|
||||
if !ok {
|
||||
t.Fatalf("Unexpected payload format in event received by moderator.\nGot: %+v\nWant: map with 'message' key", data.Payload)
|
||||
}
|
||||
if alreadySeen, exists := expectedMessagesSeen[message]; exists {
|
||||
if alreadySeen {
|
||||
t.Fatalf("Duplicate message in event received by moderator: %s", message)
|
||||
}
|
||||
expectedMessagesSeen[message] = true
|
||||
} else {
|
||||
t.Fatalf("Unexpected message in event received by moderator: %s", message)
|
||||
}
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("Timeout waiting for event to be received by moderator")
|
||||
}
|
||||
}
|
||||
for msg, seen := range expectedMessagesSeen {
|
||||
if !seen {
|
||||
t.Fatalf("Moderator did not receive expected message: %s", msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the events were not seen by the other listener
|
||||
select {
|
||||
case <-otherListener.receivedEvents:
|
||||
t.Fatal("otherListener should not have received any events")
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
// No event received, as expected
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmissionToNonExistentListener(t *testing.T) {
|
||||
// Create a test listener and register it
|
||||
listenerID := ListenerID("testListener")
|
||||
testListener := &TestListener{
|
||||
id: listenerID,
|
||||
receivedEvents: make(chan events.Event, 10),
|
||||
}
|
||||
|
||||
// Create event manager with the test listener marked as subscribed to BlacklistToggledEvents,
|
||||
// but not actually registered
|
||||
logger, err := logger.NewFmtLogger()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create logger: %v", err)
|
||||
}
|
||||
em := eventManager{
|
||||
subscriptions: map[events.EventName][]ListenerID{
|
||||
events.EventBlacklistToggled: {listenerID},
|
||||
},
|
||||
subscribers: make(map[ListenerID]Listener),
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
// Emit a BlacklistToggled event
|
||||
testEvent := &events.BlacklistToggledEvent{
|
||||
RuleID: "rule123",
|
||||
Enabled: false,
|
||||
}
|
||||
eventEmitted := make(chan struct{})
|
||||
go func() {
|
||||
em.Emit(testEvent)
|
||||
time.Sleep(10 * time.Millisecond) // Give some time for the emission to process
|
||||
close(eventEmitted)
|
||||
}()
|
||||
|
||||
// Wait for the event emission to complete
|
||||
select {
|
||||
case <-eventEmitted:
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("Timeout waiting for event emission to complete, likely due to deadlock")
|
||||
}
|
||||
|
||||
// check if the listener is still tracked by the event manager
|
||||
em.mutex.RLock()
|
||||
_, isRegistered := em.subscribers[listenerID]
|
||||
subscribers := em.subscriptions[events.EventBlacklistToggled]
|
||||
em.mutex.RUnlock()
|
||||
if len(subscribers) != 0 {
|
||||
t.Fatal("Listener should have been removed from subscriptions after failed dispatch")
|
||||
}
|
||||
if isRegistered {
|
||||
t.Fatal("Listener was somehow registered")
|
||||
}
|
||||
|
||||
// Since the listener was unregistered, it should not receive any events
|
||||
select {
|
||||
case <-testListener.receivedEvents:
|
||||
t.Fatal("Listener should not have received any events after being unregistered")
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
// No event received, as expected
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,16 @@ type EventName string
|
||||
type EventPayload interface {
|
||||
// GetName returns the event type
|
||||
GetName() EventName
|
||||
|
||||
// Returns the "source" of the event, that is, the component or plugin that emitted the event
|
||||
GetEventSource() string
|
||||
}
|
||||
|
||||
// Event represents a system event
|
||||
type Event struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"` // Unix timestamp
|
||||
UUID string `json:"uuid"` // UUID for the event
|
||||
Data EventPayload `json:"data"`
|
||||
}
|
||||
|
||||
@@ -28,6 +32,9 @@ const (
|
||||
EventBlacklistToggled EventName = "blacklistToggled"
|
||||
// EventAccessRuleCreated is emitted when a new access ruleset is created
|
||||
EventAccessRuleCreated EventName = "accessRuleCreated"
|
||||
// A custom event emitted by a plugin, with the intention of being broadcast
|
||||
// to the designated recipient(s)
|
||||
EventCustom EventName = "customEvent"
|
||||
|
||||
// Add more event types as needed
|
||||
)
|
||||
@@ -36,6 +43,7 @@ var validEventNames = map[EventName]bool{
|
||||
EventBlacklistedIPBlocked: true,
|
||||
EventBlacklistToggled: true,
|
||||
EventAccessRuleCreated: true,
|
||||
EventCustom: true,
|
||||
// Add more event types as needed
|
||||
// NOTE: Keep up-to-date with event names specified above
|
||||
}
|
||||
@@ -59,6 +67,10 @@ func (e *BlacklistedIPBlockedEvent) GetName() EventName {
|
||||
return EventBlacklistedIPBlocked
|
||||
}
|
||||
|
||||
func (e *BlacklistedIPBlockedEvent) GetEventSource() string {
|
||||
return "proxy-access"
|
||||
}
|
||||
|
||||
// BlacklistToggledEvent represents an event when the blacklist is disabled for an access rule
|
||||
type BlacklistToggledEvent struct {
|
||||
RuleID string `json:"rule_id"`
|
||||
@@ -69,6 +81,10 @@ func (e *BlacklistToggledEvent) GetName() EventName {
|
||||
return EventBlacklistToggled
|
||||
}
|
||||
|
||||
func (e *BlacklistToggledEvent) GetEventSource() string {
|
||||
return "accesslist-api"
|
||||
}
|
||||
|
||||
// AccessRuleCreatedEvent represents an event when a new access ruleset is created
|
||||
type AccessRuleCreatedEvent struct {
|
||||
ID string `json:"id"`
|
||||
@@ -82,12 +98,31 @@ func (e *AccessRuleCreatedEvent) GetName() EventName {
|
||||
return EventAccessRuleCreated
|
||||
}
|
||||
|
||||
func (e *AccessRuleCreatedEvent) GetEventSource() string {
|
||||
return "accesslist-api"
|
||||
}
|
||||
|
||||
type CustomEvent struct {
|
||||
SourcePlugin string `json:"source_plugin"`
|
||||
Recipients []string `json:"recipients"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
}
|
||||
|
||||
func (e *CustomEvent) GetName() EventName {
|
||||
return EventCustom
|
||||
}
|
||||
|
||||
func (e *CustomEvent) GetEventSource() string {
|
||||
return e.SourcePlugin
|
||||
}
|
||||
|
||||
// ParseEvent parses a JSON byte slice into an Event struct
|
||||
func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// First, determine the event type, and parse shared fields, from the JSON data
|
||||
var temp struct {
|
||||
Name EventName `json:"name"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
UUID string `json:"uuid"`
|
||||
}
|
||||
if err := json.Unmarshal(jsonData, &temp); err != nil {
|
||||
return err
|
||||
@@ -96,6 +131,7 @@ func ParseEvent(jsonData []byte, event *Event) error {
|
||||
// Set the event name and timestamp
|
||||
event.Name = temp.Name
|
||||
event.Timestamp = temp.Timestamp
|
||||
event.UUID = temp.UUID
|
||||
|
||||
// Now, based on the event type, unmarshal the specific payload
|
||||
switch temp.Name {
|
||||
@@ -126,6 +162,15 @@ func ParseEvent(jsonData []byte, event *Event) error {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
case EventCustom:
|
||||
type tempData struct {
|
||||
Data CustomEvent `json:"data"`
|
||||
}
|
||||
var payload tempData
|
||||
if err := json.Unmarshal(jsonData, &payload); err != nil {
|
||||
return err
|
||||
}
|
||||
event.Data = &payload.Data
|
||||
default:
|
||||
return fmt.Errorf("unknown event: %s, %v", temp.Name, jsonData)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user