|
- package sysevent
-
- import (
- "fmt"
- "sync"
-
- "github.com/streadway/amqp"
- "gitlink.org.cn/cloudream/common/pkgs/async"
- "gitlink.org.cn/cloudream/common/utils/lo2"
- "gitlink.org.cn/cloudream/common/utils/serder"
- )
-
- type Watcher interface {
- OnEvent(event SysEvent)
- }
-
- type WatcherEvent interface{}
-
- type WatcherExited struct {
- Err error
- }
-
- type WatcherHost struct {
- cfg Config
- watchers []Watcher
- lock sync.Mutex
- connection *amqp.Connection
- channel *amqp.Channel
- recvChan <-chan amqp.Delivery
- }
-
- func NewWatcherHost(cfg Config) (*WatcherHost, error) {
- if !cfg.Enabled {
- return &WatcherHost{
- cfg: cfg,
- }, nil
- }
-
- config := amqp.Config{
- Vhost: cfg.VHost,
- }
-
- url := fmt.Sprintf("amqp://%s:%s@%s", cfg.Account, cfg.Password, cfg.Address)
- connection, err := amqp.DialConfig(url, config)
- if err != nil {
- return nil, err
- }
-
- channel, err := connection.Channel()
- if err != nil {
- connection.Close()
- return nil, fmt.Errorf("openning channel on connection: %w", err)
- }
-
- err = channel.ExchangeDeclare(cfg.Exchange, "fanout", false, true, false, false, nil)
- if err != nil {
- connection.Close()
- return nil, fmt.Errorf("declare exchange: %w", err)
- }
-
- _, err = channel.QueueDeclare(
- cfg.Queue,
- false,
- true,
- false,
- false,
- nil,
- )
- if err != nil {
- channel.Close()
- connection.Close()
- return nil, fmt.Errorf("declare queue: %w", err)
- }
-
- err = channel.QueueBind(cfg.Queue, "", cfg.Exchange, false, nil)
- if err != nil {
- channel.Close()
- connection.Close()
- return nil, fmt.Errorf("bind queue: %w", err)
- }
-
- recvChan, err := channel.Consume(cfg.Queue, "", true, false, true, false, nil)
- if err != nil {
- channel.Close()
- connection.Close()
- return nil, fmt.Errorf("consume queue: %w", err)
- }
-
- wat := &WatcherHost{
- cfg: cfg,
- connection: connection,
- channel: channel,
- recvChan: recvChan,
- }
-
- return wat, nil
- }
-
- func (w *WatcherHost) Start() *async.UnboundChannel[WatcherEvent] {
- ch := async.NewUnboundChannel[WatcherEvent]()
-
- go func() {
- if !w.cfg.Enabled {
- return
- }
-
- defer ch.Close()
- defer w.channel.Close()
- defer w.connection.Close()
-
- for m := range w.recvChan {
- evt, err := serder.JSONToObjectEx[SysEvent](m.Body)
- if err != nil {
- ch.Send(OtherError{Err: fmt.Errorf("deserialize event: %w", err)})
- continue
- }
-
- w.lock.Lock()
- ws := make([]Watcher, 0, len(w.watchers))
- ws = append(ws, w.watchers...)
- w.lock.Unlock()
-
- for _, w := range ws {
- w.OnEvent(evt)
- }
- }
-
- ch.Send(WatcherExited{Err: nil})
- }()
-
- return ch
- }
-
- func (w *WatcherHost) Stop() {
- if !w.cfg.Enabled {
- return
- }
-
- w.channel.Close()
- w.connection.Close()
- }
-
- func (w *WatcherHost) Enabled() bool {
- return w.cfg.Enabled
- }
-
- func (w *WatcherHost) AddWatcher(watcher Watcher) {
- w.lock.Lock()
- defer w.lock.Unlock()
-
- w.watchers = append(w.watchers, watcher)
- }
-
- func (w *WatcherHost) AddWatcherFn(fn func(event SysEvent)) Watcher {
- watcher := &fnWatcher{fn: fn}
- w.AddWatcher(watcher)
- return watcher
- }
-
- func (w *WatcherHost) RemoveWatcher(watcher Watcher) {
- w.lock.Lock()
- defer w.lock.Unlock()
-
- w.watchers = lo2.Remove(w.watchers, watcher)
- }
-
- type fnWatcher struct {
- fn func(event SysEvent)
- }
-
- func (w *fnWatcher) OnEvent(event SysEvent) {
- w.fn(event)
- }
|