bluesky appview implementation using microcosm and other services server.reddwarf.app
appview bluesky reddwarf microcosm
at main 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}