feat: notify document updates over websocket
This commit is contained in:
70
apps/server/internal/realtime/hub.go
Normal file
70
apps/server/internal/realtime/hub.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package realtime
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Event struct {
|
||||
Type string `json:"type"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
send chan []byte
|
||||
}
|
||||
|
||||
type Hub struct {
|
||||
logger *slog.Logger
|
||||
mu sync.RWMutex
|
||||
clients map[*Client]struct{}
|
||||
}
|
||||
|
||||
func NewHub(logger *slog.Logger) *Hub {
|
||||
return &Hub{
|
||||
logger: logger,
|
||||
clients: make(map[*Client]struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hub) NewClient(buffer int) *Client {
|
||||
return &Client{send: make(chan []byte, buffer)}
|
||||
}
|
||||
|
||||
func (h *Hub) Register(client *Client) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
h.clients[client] = struct{}{}
|
||||
}
|
||||
|
||||
func (h *Hub) Unregister(client *Client) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
if _, ok := h.clients[client]; ok {
|
||||
delete(h.clients, client)
|
||||
close(client.send)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Hub) Broadcast(event Event) {
|
||||
payload, err := json.Marshal(event)
|
||||
if err != nil {
|
||||
h.logger.Error("marshal realtime event", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
h.mu.RLock()
|
||||
defer h.mu.RUnlock()
|
||||
for client := range h.clients {
|
||||
select {
|
||||
case client.send <- payload:
|
||||
default:
|
||||
h.logger.Warn("dropping realtime event for slow client")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Send() <-chan []byte {
|
||||
return c.send
|
||||
}
|
||||
Reference in New Issue
Block a user