events.go
package websites
import "sync"
// Event represents a status update for a site.
type Event struct {
Status string `json:"status"`
Message string `json:"message"`
}
var (
mu sync.Mutex
subscribers = map[string][]chan Event{}
)
// Subscribe returns a channel that receives events for a site.
func Subscribe(siteID string) chan Event {
mu.Lock()
defer mu.Unlock()
ch := make(chan Event, 16)
subscribers[siteID] = append(subscribers[siteID], ch)
return ch
}
// Unsubscribe removes a channel and closes it.
// Safe to call multiple times — only closes the channel once.
func Unsubscribe(siteID string, ch chan Event) {
mu.Lock()
defer mu.Unlock()
subs := subscribers[siteID]
found := false
for i, sub := range subs {
if sub == ch {
subscribers[siteID] = append(subs[:i], subs[i+1:]...)
found = true
break
}
}
if len(subscribers[siteID]) == 0 {
delete(subscribers, siteID)
}
// Only close if the channel was still subscribed (prevents double-close panic)
if found {
close(ch)
}
}
// publish sends an event to all subscribers for a site.
func publish(siteID string, event Event) {
mu.Lock()
defer mu.Unlock()
for _, ch := range subscribers[siteID] {
select {
case ch <- event:
default: // drop if subscriber is slow
}
}
}