Documentation
¶
Overview ¶
Package realtime is an in-process WebSocket pub/sub hub for pushing server-side events (logs, sync progress) to the browser. It's mounted directly on the existing Fiber app — no separate listener/port, and no dependency beyond github.com/gofiber/contrib/v3/websocket (itself a thin fasthttp-native wrapper, so this adds nothing indirect beyond what Fiber already pulls in).
Mounting:
node := realtime.New(logger)
app.Get("/ws", node.Handler())
Publishing:
// pre-marshaled JSON, e.g. from logger.BrowserHandler go node.DrainLogs(ctx, browserHandler.Chan()) // any JSON-marshalable type progressCh := make(chan service.ProgressEvent, 1024) go realtime.Drain(node, ctx, realtime.ProgressChannel, progressCh)
Wire protocol (JSON, one message per WebSocket frame, ws://<host>/ws — same origin/port as the rest of the app):
Client -> Server:
{"type":"subscribe","channel":"logs"}
{"type":"unsubscribe","channel":"logs"}
Server -> Client:
{"type":"subscribed","channel":"logs"}
{"type":"unsubscribed","channel":"logs"}
{"type":"publication","channel":"logs","data":{...}}
{"type":"error","error":"..."}
A client that never subscribes to anything just sits idle (ping/pong keepalive only) — subscribing is required to receive any "publication".
Index ¶
Constants ¶
const ( LogChannel = "logs" ProgressChannel = "sync_progress" )
Channel names in use by this application. Publish/Drain accept any string as a channel — these two are just what's currently published.
Variables ¶
This section is empty.
Functions ¶
func Drain ¶
Drain reads typed values from ch, JSON-marshals each one, and Publishes the result on channel. Runs until ctx is cancelled or ch is closed; intended to be called in a goroutine. It's a free function rather than a method because Go doesn't allow generic methods on a non-generic receiver. Marshal failures are logged and skipped rather than aborting the drain loop — one bad event shouldn't kill the feed.
Don't use this for already-marshaled []byte payloads (like log entries) — json.Marshal on a []byte base64-encodes it instead of passing it through. Use DrainLogs for those.
Types ¶
type Node ¶
type Node struct {
// contains filtered or unexported fields
}
Node is the pub/sub hub. Create one with New and mount Handler() on a route; it needs no Start/Stop of its own since it owns no listener — call Close() during graceful shutdown to disconnect clients.
func New ¶
New creates a Node. It doesn't open any network resources itself — the Fiber app the returned Handler is mounted on does that.
func (*Node) ClientCount ¶
ClientCount returns the number of currently connected WebSocket clients. Handy for a health/debug endpoint.
func (*Node) Close ¶
func (n *Node) Close()
Close disconnects every currently connected client. Call this during graceful shutdown, before or alongside app.Shutdown().
func (*Node) DrainLogs ¶
DrainLogs reads from ch and publishes each entry to LogChannel. ch already carries pre-marshaled JSON (see logger.BrowserHandler.Handle), so this forwards the bytes as-is instead of going through Drain/json.Marshal (which would base64-encode them). Runs until ctx is cancelled or ch is closed; intended to be called in a goroutine.
func (*Node) Handler ¶
Handler returns the fiber.Handler to mount on your WebSocket route, e.g. app.Get("/ws", node.Handler()). Origins are intentionally left at the contrib package's default (allow all): if this stops being localhost-only reachable, add websocket.Config{Origins: [...]}.