package sticket import ( "log" "net/http" "sync" "github.com/google/uuid" "github.com/gorilla/websocket" ) const HeaderKey = "X-STICKET" type Event struct { Type string `json:"type"` Payload any `json:"payload"` } type Manager struct { clients map[string]*websocket.Conn mu sync.RWMutex upgrader websocket.Upgrader } func New() *Manager { return &Manager{ clients: make(map[string]*websocket.Conn), upgrader: websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true }, }, } } func (m *Manager) HandleWS(w http.ResponseWriter, r *http.Request) { conn, err := m.upgrader.Upgrade(w, r, nil) if err != nil { log.Printf("Sticket: Failed to upgrade: %v", err) return } clientUUID := r.URL.Query().Get("uuid") //isNew := false if clientUUID == "" { //isNew = true clientUUID = uuid.New().String() } else { if _, err := uuid.Parse(clientUUID); err != nil { //isNew = true clientUUID = uuid.New().String() } } m.register(clientUUID, conn) defer m.unregister(clientUUID) initEvent := Event{ Type: "init", Payload: map[string]string{"uuid": clientUUID}, } if err := m.sendJson(conn, initEvent); err != nil { return } for { if _, _, err := conn.NextReader(); err != nil { break } } } func (m *Manager) SendToClient(uuid string, eventType string, data any) bool { m.mu.RLock() conn, ok := m.clients[uuid] m.mu.RUnlock() if !ok { return false } evt := Event{ Type: eventType, Payload: data, } m.mu.Lock() defer m.mu.Unlock() if conn, ok = m.clients[uuid]; !ok { return false } if err := m.sendJson(conn, evt); err != nil { conn.Close() delete(m.clients, uuid) return false } return true } func GetUUIDFromRequest(r *http.Request) string { return r.Header.Get(HeaderKey) } func (m *Manager) register(id string, conn *websocket.Conn) { m.mu.Lock() defer m.mu.Unlock() if existing, ok := m.clients[id]; ok { existing.Close() } m.clients[id] = conn log.Printf("Sticket: Client connected [%s]", id) } func (m *Manager) unregister(id string) { m.mu.Lock() defer m.mu.Unlock() if conn, ok := m.clients[id]; ok { conn.Close() delete(m.clients, id) log.Printf("Sticket: Client disconnected [%s]", id) } } func (m *Manager) sendJson(conn *websocket.Conn, v any) error { return conn.WriteJSON(v) }