github.com/e154/smart-home@v0.17.2-0.20240311175135-e530a6e5cd45/system/stream/stream.go (about) 1 // This file is part of the Smart Home 2 // Program complex distribution https://github.com/e154/smart-home 3 // Copyright (C) 2016-2023, Filippov Alex 4 // 5 // This library is free software: you can redistribute it and/or 6 // modify it under the terms of the GNU Lesser General Public 7 // License as published by the Free Software Foundation; either 8 // version 3 of the License, or (at your option) any later version. 9 // 10 // This library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 // Library General Public License for more details. 14 // 15 // You should have received a copy of the GNU Lesser General Public 16 // License along with this library. If not, see 17 // <https://www.gnu.org/licenses/>. 18 19 package stream 20 21 import ( 22 "context" 23 "sync" 24 25 "github.com/google/uuid" 26 "github.com/gorilla/websocket" 27 "go.uber.org/fx" 28 29 "github.com/e154/smart-home/common/events" 30 "github.com/e154/smart-home/common/logger" 31 m "github.com/e154/smart-home/models" 32 "github.com/e154/smart-home/system/bus" 33 ) 34 35 var ( 36 log = logger.MustGetLogger("stream") 37 ) 38 39 var ( 40 upgrader = websocket.Upgrader{} 41 ) 42 43 // Stream ... 44 type Stream struct { 45 *eventHandler 46 subMx sync.Mutex 47 subscribers map[string]func(client IStreamClient, id string, msg []byte) 48 sessions sync.Map 49 eventBus bus.Bus 50 } 51 52 // NewStreamService ... 53 func NewStreamService(lc fx.Lifecycle, 54 eventBus bus.Bus) (s *Stream) { 55 s = &Stream{ 56 subscribers: make(map[string]func(client IStreamClient, id string, msg []byte)), 57 sessions: sync.Map{}, 58 eventBus: eventBus, 59 } 60 61 s.eventHandler = NewEventHandler(s.Broadcast, s.DirectMessage) 62 63 lc.Append(fx.Hook{ 64 OnStart: func(ctx context.Context) (err error) { 65 return s.Start(ctx) 66 }, 67 OnStop: func(ctx context.Context) (err error) { 68 return s.Shutdown(ctx) 69 }, 70 }) 71 72 return 73 } 74 75 // Start ... 76 func (s *Stream) Start(_ context.Context) error { 77 _ = s.eventBus.Subscribe("system/#", s.eventHandler.eventHandler) 78 s.eventBus.Publish("system/services/stream", events.EventServiceStarted{Service: "Stream"}) 79 return nil 80 } 81 82 // Shutdown ... 83 func (s *Stream) Shutdown(_ context.Context) error { 84 _ = s.eventBus.Unsubscribe("system/#", s.eventHandler.eventHandler) 85 _ = s.eventBus.Unsubscribe("system/dashboard", s.eventHandler.eventHandler) 86 s.sessions.Range(func(key, value interface{}) bool { 87 cli := value.(*Client) 88 cli.Close() 89 return true 90 }) 91 s.eventBus.Publish("system/services/stream", events.EventServiceStopped{Service: "Stream"}) 92 return nil 93 } 94 95 // Broadcast ... 96 func (s *Stream) Broadcast(query string, message []byte) { 97 s.sessions.Range(func(key, value interface{}) bool { 98 cli := value.(*Client) 99 _ = cli.Send(uuid.NewString(), query, message) 100 return true 101 }) 102 } 103 104 // DirectMessage ... 105 func (s *Stream) DirectMessage(userID int64, sessionID string, query string, message []byte) { 106 s.sessions.Range(func(key, value interface{}) bool { 107 cli, ok := value.(*Client) 108 if !ok { 109 return false 110 } 111 if sessionID != "" && cli.SessionID() == sessionID { 112 _ = cli.Send(uuid.NewString(), query, message) 113 return true 114 } 115 if sessionID == "" && cli.user.Id == userID { 116 _ = cli.Send(uuid.NewString(), query, message) 117 } 118 return true 119 }) 120 } 121 122 // Subscribe ... 123 func (s *Stream) Subscribe(command string, f func(IStreamClient, string, []byte)) { 124 log.Infof("subscribe %s", command) 125 s.subMx.Lock() 126 defer s.subMx.Unlock() 127 if s.subscribers[command] != nil { 128 delete(s.subscribers, command) 129 } 130 s.subscribers[command] = f 131 132 } 133 134 // UnSubscribe ... 135 func (s *Stream) UnSubscribe(command string) { 136 log.Infof("unsubscribe %s", command) 137 s.subMx.Lock() 138 defer s.subMx.Unlock() 139 if s.subscribers[command] != nil { 140 delete(s.subscribers, command) 141 } 142 } 143 144 // NewConnection ... 145 func (s *Stream) NewConnection(ws *websocket.Conn, user *m.User) { 146 147 id := uuid.NewString() 148 client := NewClient(ws, user, id) 149 defer func() { 150 log.Infof("websocket session closed, email: '%s'", user.Email) 151 s.sessions.Delete(id) 152 }() 153 154 s.sessions.Store(id, client) 155 log.Infof("new websocket session established, email: '%s'", user.Email) 156 157 client.WritePump(s.Recv) 158 } 159 160 // Recv ... 161 func (s *Stream) Recv(client *Client, id, query string, b []byte) { 162 //log.Debugf("id: %s, query: %s, body: %s", id, query, string(b)) 163 s.subMx.Lock() 164 f, ok := s.subscribers[query] 165 s.subMx.Unlock() 166 if ok { 167 f(client, id, b) 168 } 169 }