feat(11-03): add the lighthouse realtime package and its Centrifugo driver

- lighthouse: Service/From with realtime.driver selection, RegisterDriver
  registry, null/log/memory drivers, Route/Surface/Mount, users and actors
- centrifugo: HTTP API client (apikey header, 2xx success, no request
  without a key), five-generator HS256 TokenIssuer, TokenHandler with the
  WinterCMS 401/503 bodies
- module README and root modules table row
This commit is contained in:
Jakub Zych
2026-09-30 12:18:11 +02:00
parent f1077382f5
commit cada7a4442
11 changed files with 1302 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
// Package centrifugo is the Centrifugo driver of lighthouse: an HTTP API
// client, a connection and subscription token issuer, and the subscribe
// proxy handler. Import it for its side effect to register the
// "centrifugo" realtime.driver.
package centrifugo
import (
"strings"
"time"
"git.golem15.com/golem15/summercms/modules/compass"
"git.golem15.com/golem15/summercms/modules/lighthouse"
)
// Default values of the realtime.centrifugo.* keys.
const (
DefaultAPIURL = "http://127.0.0.1:8001/api"
DefaultTokenTTL = 3600 * time.Second
DefaultWSURL = "/ws"
DefaultTokenPath = "/api/realtime/token"
DefaultSubscribePath = "/api/realtime/subscribe"
)
// Config is the realtime.centrifugo.* configuration.
type Config struct {
// APIURL is the Centrifugo HTTP API base, without a trailing method.
APIURL string
// APIKey authenticates publishes; empty disables them.
APIKey string
// TokenSecret signs connection and subscription tokens (HS256); empty
// makes the token route answer 503.
TokenSecret string
// TokenTTL is the lifetime of issued tokens.
TokenTTL time.Duration
// WSURL is the WebSocket URL the client connects to.
WSURL string
// ProxySecret is the X-Centrifugo-Secret header value the subscribe
// proxy expects; empty denies every subscribe.
ProxySecret string
// TokenPath and SubscribePath are the mounted route paths.
TokenPath string
SubscribePath string
}
// LoadConfig reads realtime.centrifugo.* from c, filling the defaults.
// token_ttl is an integer number of seconds or a duration string.
func LoadConfig(c *compass.Config) Config {
cfg := Config{
APIURL: DefaultAPIURL,
TokenTTL: DefaultTokenTTL,
WSURL: DefaultWSURL,
TokenPath: DefaultTokenPath,
SubscribePath: DefaultSubscribePath,
}
if c == nil {
return cfg
}
str := func(key string) string { return strings.TrimSpace(c.String("realtime.centrifugo." + key)) }
if v := str("api_url"); v != "" {
cfg.APIURL = strings.TrimSuffix(v, "/")
}
cfg.APIKey = str("api_key")
cfg.TokenSecret = str("token_secret")
cfg.ProxySecret = str("proxy_secret")
if d := lighthouse.DurationSetting(c, "realtime.centrifugo.token_ttl"); d > 0 {
cfg.TokenTTL = d
}
if v := str("ws_url"); v != "" {
cfg.WSURL = v
}
if v := str("token_path"); v != "" {
cfg.TokenPath = v
}
if v := str("subscribe_path"); v != "" {
cfg.SubscribePath = v
}
return cfg
}