transmit

package
v1.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 16, 2026 License: MIT Imports: 12 Imported by: 0

README

Transmit Plugin for Nimbus

Server-Sent Events (SSE) for real-time server-to-client push. Inspired by AdonisJS Transmit.

Installation

Transmit is a default plugin when creating a new app with nimbus new. To add manually:

nimbus add transmit

Or in bin/server.go:

import "github.com/CodeSyncr/nimbus/plugins/transmit"

app.Use(transmit.New(nil))

Configuration

Variable Description Default
TRANSMIT_PATH Route prefix __transmit
TRANSMIT_PING_INTERVAL Keep-alive ping (e.g. 30s, 1m) disabled
TRANSMIT_TRANSPORT Multi-instance: redis none
REDIS_URL Redis URL (for transport) redis://localhost:6379
TRANSMIT_REDIS_CHANNEL Redis pub/sub channel transmit::broadcast
Code-based config
app.Use(transmit.New(&transmit.Config{
    Path:         "__transmit",
    PingInterval: "30s",
    Middleware:   []router.Middleware{authMiddleware},  // optional
    Transport:    transmit.NewRedisTransport(transmit.RedisTransportConfig{}),  // multi-instance
}))

Routes

Route Method Purpose
__transmit/events GET Establishes SSE connection
__transmit/subscribe POST Subscribe to channel
__transmit/unsubscribe POST Unsubscribe from channel

Usage

Broadcasting
import "github.com/CodeSyncr/nimbus/plugins/transmit"

func createPost(c *http.Context) error {
    post := createPostFromRequest(c)
    transmit.Broadcast("posts", map[string]any{
        "id":    post.ID,
        "title": post.Title,
    })
    return c.JSON(201, post)
}

// Exclude sender from receiving their own message
transmit.BroadcastExcept("chats/1/messages", data, senderUID)
Channel authorization

Restrict access to private channels:

import (
    "fmt"
    "github.com/CodeSyncr/nimbus/http"
    "github.com/CodeSyncr/nimbus/plugins/transmit"
)

func init() {
    transmit.Authorize("users/:id", func(ctx *http.Context, params map[string]string) bool {
        userID, ok := ctx.Get("user_id")  // from your auth middleware
        return ok && fmt.Sprint(userID) == params["id"]
    })
}
Lifecycle hooks
transmit.OnConnect(func(uid string) { log.Printf("Client %s connected", uid) })
transmit.OnDisconnect(func(uid string) { log.Printf("Client %s disconnected", uid) })
transmit.OnSubscribe(func(uid, channel string) { /* ... */ })
transmit.OnUnsubscribe(func(uid, channel string) { /* ... */ })
transmit.OnBroadcast(func(channel string, payload any) { /* ... */ })
Get subscribers
uids := transmit.GetSubscribers("chats/1/messages")

Client setup

Clients connect to GET /__transmit/events, receive a UID in the first message, then POST to subscribe:

const es = new EventSource('/__transmit/events');
es.onmessage = (e) => {
  const data = JSON.parse(e.data);
  if (data.uid) {
    fetch('/__transmit/subscribe', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ channel: 'notifications', uid: data.uid })
    });
  } else {
    console.log('Message:', data);
  }
};

Or use @adonisjs/transmit-client with baseUrl pointing to your Nimbus server.

Multi-instance (Redis transport)

When running multiple instances behind a load balancer, set TRANSMIT_TRANSPORT=redis and REDIS_URL. Broadcasts will sync across all instances via Redis Pub/Sub.

Production

Disable compression for text/event-stream in your reverse proxy:

  • Nginx: Exclude text/event-stream from gzip_types
  • Traefik: excludedcontenttypes=text/event-stream

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Authorize

func Authorize(pattern string, fn AuthorizeFunc)

Authorize registers an authorization callback for channels matching pattern. Pattern uses :param syntax (e.g. "users/:id", "chats/:chatId/messages"). Return true to allow, false to deny. Channels without a matching rule are public.

func Broadcast

func Broadcast(channel string, payload any)

Broadcast sends payload to all subscribers of channel. With transport configured, publishes to Redis so all instances deliver.

func BroadcastExcept

func BroadcastExcept(channel string, payload any, excludeUIDs ...string)

BroadcastExcept sends payload to all subscribers except the given UIDs.

func CheckChannel

func CheckChannel(ctx *reqctx.Context, channel string) bool

CheckChannel returns true if the client may subscribe to channel.

func GetSubscribers

func GetSubscribers(channel string) []string

GetSubscribers returns UIDs subscribed to channel (AdonisJS getSubscribersFor).

func OnBroadcast

func OnBroadcast(fn BroadcastHook)

OnBroadcast registers a callback when a message is broadcast.

func OnConnect

func OnConnect(fn ConnectHook)

OnConnect registers a callback for new SSE connections.

func OnDisconnect

func OnDisconnect(fn DisconnectHook)

OnDisconnect registers a callback when a client disconnects.

func OnSubscribe

func OnSubscribe(fn SubscribeHook)

OnSubscribe registers a callback when a client subscribes to a channel.

func OnUnsubscribe

func OnUnsubscribe(fn UnsubscribeHook)

OnUnsubscribe registers a callback when a client unsubscribes.

Types

type AuthorizeFunc

type AuthorizeFunc func(ctx *reqctx.Context, params map[string]string) bool

AuthorizeFunc returns true to allow subscription, false to deny.

type BroadcastHook

type BroadcastHook func(channel string, payload any)

type Client

type Client struct {
	UID    string
	Events chan []byte
	Done   chan struct{}
}

Client represents an SSE-connected client.

type Config

type Config struct {
	Path         string              // route prefix, default __transmit
	PingInterval string              // e.g. "30s", "1m", empty to disable
	Middleware   []router.Middleware // optional middleware for all transmit routes (e.g. auth)
	Transport    Transport           // optional; Redis for multi-instance sync
}

Config holds Transmit configuration.

type ConnectHook

type ConnectHook func(uid string)

type DisconnectHook

type DisconnectHook func(uid string)

type Plugin

type Plugin struct {
	nimbus.BasePlugin
	// contains filtered or unexported fields
}

Plugin integrates SSE into Nimbus.

func New

func New(cfg *Config) *Plugin

New creates a new Transmit plugin.

func (*Plugin) Boot

func (p *Plugin) Boot(app *nimbus.App) error

Boot starts the transport subscriber when configured.

func (*Plugin) Register

func (p *Plugin) Register(app *nimbus.App) error

Register sets the global store and transport.

func (*Plugin) RegisterRoutes

func (p *Plugin) RegisterRoutes(r *router.Router)

RegisterRoutes mounts SSE routes.

func (*Plugin) Shutdown

func (p *Plugin) Shutdown() error

Shutdown closes the transport when configured.

type RedisTransport

type RedisTransport struct {
	// contains filtered or unexported fields
}

RedisTransport uses Redis Pub/Sub for multi-instance sync.

func NewRedisTransport

func NewRedisTransport(cfg RedisTransportConfig) (*RedisTransport, error)

NewRedisTransport creates a Redis transport.

func (*RedisTransport) Close

func (t *RedisTransport) Close() error

Close closes the pubsub and client.

func (*RedisTransport) Publish

func (t *RedisTransport) Publish(ctx context.Context, channel string, payload any, excludeUIDs []string) error

Publish publishes to Redis; all subscribers (including self) receive.

func (*RedisTransport) Subscribe

func (t *RedisTransport) Subscribe(ctx context.Context, onMessage func(channel string, payload any, excludeUIDs []string)) error

Subscribe subscribes to the Redis channel and invokes onMessage for each broadcast.

type RedisTransportConfig

type RedisTransportConfig struct {
	URL     string // redis://localhost:6379
	Channel string // default transmit::broadcast
}

RedisTransportConfig configures the Redis transport.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store holds connections and channel subscriptions.

func GetStore

func GetStore() *Store

GetStore returns the global store (for Broadcast).

func NewStore

func NewStore() *Store

NewStore creates a new transmit store.

func (*Store) Connect

func (s *Store) Connect() (string, *Client)

Connect adds a new client and returns its UID.

func (*Store) DeliverToChannel

func (s *Store) DeliverToChannel(channel string, payload any, excludeUIDs ...string)

DeliverToChannel delivers to local subscribers without emitting OnBroadcast. Used by transport when relaying from other instances.

func (*Store) Disconnect

func (s *Store) Disconnect(uid string)

Disconnect removes a client and unsubscribes from all channels.

func (*Store) SendToChannel

func (s *Store) SendToChannel(channel string, payload any, excludeUIDs ...string)

SendToChannel sends payload to all subscribers of channel, optionally excluding UIDs. Emits OnBroadcast. Use DeliverToChannel when relaying from transport (no emit).

func (*Store) Subscribe

func (s *Store) Subscribe(channel, uid string)

Subscribe adds uid to channel.

func (*Store) Subscribers

func (s *Store) Subscribers(channel string) []string

Subscribers returns UIDs subscribed to channel.

func (*Store) Unsubscribe

func (s *Store) Unsubscribe(channel, uid string)

Unsubscribe removes uid from channel.

type SubscribeHook

type SubscribeHook func(uid, channel string)

type Transport

type Transport interface {
	// Publish sends channel+payload to all instances (including self via subscription).
	Publish(ctx context.Context, channel string, payload any, excludeUIDs []string) error
	// Subscribe starts receiving published messages. Call from Boot. Blocking.
	Subscribe(ctx context.Context, onMessage func(channel string, payload any, excludeUIDs []string)) error
	// Close stops the transport.
	Close() error
}

Transport synchronizes broadcasts across server instances.

type UnsubscribeHook

type UnsubscribeHook func(uid, channel string)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL