collection of golang services under the Red Dwarf umbrella server.reddwarf.app
bluesky reddwarf microcosm appview
16
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v0.0.2 130 lines 2.4 kB view raw
1package sticket 2 3import ( 4 "log" 5 "net/http" 6 "sync" 7 8 "github.com/google/uuid" 9 "github.com/gorilla/websocket" 10) 11 12const HeaderKey = "X-STICKET" 13 14type Event struct { 15 Type string `json:"type"` 16 Payload any `json:"payload"` 17} 18 19type Manager struct { 20 clients map[string]*websocket.Conn 21 mu sync.RWMutex 22 upgrader websocket.Upgrader 23} 24 25func New() *Manager { 26 return &Manager{ 27 clients: make(map[string]*websocket.Conn), 28 upgrader: websocket.Upgrader{ 29 CheckOrigin: func(r *http.Request) bool { return true }, 30 }, 31 } 32} 33 34func (m *Manager) HandleWS(w *http.ResponseWriter, r *http.Request) { 35 conn, err := m.upgrader.Upgrade(*w, r, nil) 36 if err != nil { 37 log.Printf("Sticket: Failed to upgrade: %v", err) 38 return 39 } 40 41 clientUUID := r.URL.Query().Get("uuid") 42 //isNew := false 43 44 if clientUUID == "" { 45 //isNew = true 46 clientUUID = uuid.New().String() 47 } else { 48 if _, err := uuid.Parse(clientUUID); err != nil { 49 //isNew = true 50 clientUUID = uuid.New().String() 51 } 52 } 53 54 m.register(clientUUID, conn) 55 56 defer m.unregister(clientUUID) 57 58 initEvent := Event{ 59 Type: "init", 60 Payload: map[string]string{"uuid": clientUUID}, 61 } 62 if err := m.sendJson(conn, initEvent); err != nil { 63 return 64 } 65 66 for { 67 if _, _, err := conn.NextReader(); err != nil { 68 break 69 } 70 } 71} 72 73func (m *Manager) SendToClient(uuid string, eventType string, data any) bool { 74 m.mu.RLock() 75 conn, ok := m.clients[uuid] 76 m.mu.RUnlock() 77 78 if !ok { 79 return false 80 } 81 82 evt := Event{ 83 Type: eventType, 84 Payload: data, 85 } 86 87 m.mu.Lock() 88 defer m.mu.Unlock() 89 90 if conn, ok = m.clients[uuid]; !ok { 91 return false 92 } 93 94 if err := m.sendJson(conn, evt); err != nil { 95 conn.Close() 96 delete(m.clients, uuid) 97 return false 98 } 99 100 return true 101} 102 103func GetUUIDFromRequest(r *http.Request) string { 104 return r.Header.Get(HeaderKey) 105} 106 107func (m *Manager) register(id string, conn *websocket.Conn) { 108 m.mu.Lock() 109 defer m.mu.Unlock() 110 111 if existing, ok := m.clients[id]; ok { 112 existing.Close() 113 } 114 m.clients[id] = conn 115 log.Printf("Sticket: Client connected [%s]", id) 116} 117 118func (m *Manager) unregister(id string) { 119 m.mu.Lock() 120 defer m.mu.Unlock() 121 if conn, ok := m.clients[id]; ok { 122 conn.Close() 123 delete(m.clients, id) 124 log.Printf("Sticket: Client disconnected [%s]", id) 125 } 126} 127 128func (m *Manager) sendJson(conn *websocket.Conn, v any) error { 129 return conn.WriteJSON(v) 130}