backend/pkg/library/centrifugo/centrifugo.go

188 lines
5.3 KiB
Go
Raw Normal View History

2026-01-06 07:15:18 +00:00
// Package centrifugo 提供 Centrifugo 即時訊息服務的完整 Go 客戶端
//
// 功能包含:
// - HTTP API 客戶端(發布訊息、訂閱管理、在線狀態等)
// - JWT Token 生成(連線認證、私有頻道訂閱)
// - Token 黑名單管理(撤銷單一 Token、撤銷用戶所有 Token
// - 在線狀態追蹤Redis 或記憶體存儲)
//
// 基本使用:
//
// // 創建服務實例
// svc := centrifugo.NewService(centrifugo.ServiceConfig{
// APIURL: "http://localhost:8000",
// APIKey: "your-api-key",
// TokenSecret: "your-jwt-secret",
// Redis: redisClient, // 可選,用於黑名單和在線狀態
// })
//
// // 發布訊息
// svc.Client().PublishJSON(ctx, "chat:room-1", data)
//
// // 生成 Token
// token, _ := svc.Token().QuickConnectionToken("user-123")
//
// // 撤銷用戶所有 Token 並踢出
// svc.InvalidateUser(ctx, "user-123")
package centrifugo
import (
"context"
"time"
"github.com/zeromicro/go-zero/core/stores/redis"
)
// Service Centrifugo 服務整合介面
// 提供 HTTP API、Token 生成、黑名單管理、在線狀態追蹤的統一入口
type Service struct {
client *Client
token *TokenGenerator
blacklist *TokenBlacklist
online *OnlineManager
}
// ServiceConfig 服務配置
type ServiceConfig struct {
// APIURL Centrifugo HTTP API 地址(必填)
APIURL string
// APIKey Centrifugo API 密鑰(必填)
APIKey string
// TokenSecret JWT Token 簽名密鑰(必填)
TokenSecret string
// TokenExpire Token 過期時間(預設 1 小時)
TokenExpire time.Duration
// Redis 客戶端(可選,用於黑名單和在線狀態)
Redis *redis.Redis
// ClientConfig HTTP 客戶端配置(可選)
ClientConfig *ClientConfig
// OnlineTTL 在線狀態過期時間(預設 5 分鐘)
OnlineTTL time.Duration
// KeyPrefix Redis key 前綴(預設 "centrifugo:"
KeyPrefix string
}
// NewService 創建 Centrifugo 服務實例
func NewService(cfg ServiceConfig) *Service {
// 設定預設值
if cfg.TokenExpire == 0 {
cfg.TokenExpire = time.Hour
}
if cfg.OnlineTTL == 0 {
cfg.OnlineTTL = 5 * time.Minute
}
if cfg.KeyPrefix == "" {
cfg.KeyPrefix = "centrifugo:"
}
// 創建 HTTP 客戶端
var client *Client
if cfg.ClientConfig != nil {
client = NewClientWithConfig(*cfg.ClientConfig)
} else {
client = NewClient(cfg.APIURL, cfg.APIKey)
}
// 創建 Token 生成器
token := NewTokenGeneratorWithConfig(TokenConfig{
Secret: cfg.TokenSecret,
ExpireIn: cfg.TokenExpire,
})
svc := &Service{
client: client,
token: token,
}
// 如果有 Redis創建黑名單管理器和在線狀態管理器
if cfg.Redis != nil {
svc.blacklist = NewTokenBlacklistWithPrefix(cfg.Redis, cfg.KeyPrefix+"blacklist:")
store := NewRedisOnlineStoreWithPrefix(cfg.Redis, cfg.KeyPrefix+"online:")
svc.online = NewOnlineManagerWithTTL(client, store, cfg.OnlineTTL)
}
return svc
}
// Client 返回 HTTP API 客戶端
func (s *Service) Client() *Client {
return s.client
}
// Token 返回 Token 生成器
func (s *Service) Token() *TokenGenerator {
return s.token
}
// Blacklist 返回黑名單管理器(可能為 nil
func (s *Service) Blacklist() *TokenBlacklist {
return s.blacklist
}
// Online 返回在線狀態管理器(可能為 nil
func (s *Service) Online() *OnlineManager {
return s.online
}
// ==================== 便捷方法 ====================
// PublishJSON 發布 JSON 訊息到頻道
func (s *Service) PublishJSON(ctx context.Context, channel string, data interface{}) (*PublishResult, error) {
return s.client.PublishJSON(ctx, channel, data)
}
// BroadcastJSON 批量發布 JSON 訊息到多個頻道
func (s *Service) BroadcastJSON(ctx context.Context, channels []string, data interface{}) error {
return s.client.BroadcastJSON(ctx, channels, data)
}
// Disconnect 斷開用戶連線
func (s *Service) Disconnect(ctx context.Context, userID string) error {
return s.client.Disconnect(ctx, userID)
}
// GenerateToken 快速生成連線 Token
func (s *Service) GenerateToken(userID string) (string, error) {
return s.token.QuickConnectionToken(userID)
}
// GenerateTokenWithInfo 生成帶用戶資訊的連線 Token
func (s *Service) GenerateTokenWithInfo(userID string, info map[string]interface{}) (string, error) {
return s.token.GenerateConnectionToken(ConnectionTokenOptions{
UserID: userID,
Info: info,
})
}
// InvalidateUser 撤銷用戶所有 Token 並斷開連線
// 這是最常用的「踢人」方法,適用於:
// - 用戶被封禁
// - 密碼變更
// - 用戶登出(全設備)
func (s *Service) InvalidateUser(ctx context.Context, userID string) error {
// 撤銷所有 Token
if s.blacklist != nil {
if err := s.blacklist.RevokeUserTokens(ctx, userID); err != nil {
return err
}
}
// 斷開連線
return s.client.Disconnect(ctx, userID)
}
// IsUserOnline 檢查用戶是否在線
func (s *Service) IsUserOnline(ctx context.Context, userID string) (bool, error) {
if s.online == nil {
return false, ErrOnlineStoreNotConfigured
}
return s.online.IsUserOnline(ctx, userID)
}
// GetUsersOnlineStatus 批量獲取用戶在線狀態
func (s *Service) GetUsersOnlineStatus(ctx context.Context, userIDs []string) (map[string]bool, error) {
if s.online == nil {
return nil, ErrOnlineStoreNotConfigured
}
return s.online.GetUsersOnlineStatus(ctx, userIDs)
}