Files
backend/internal/wire/infrastructure.go
lafay 6d335e393d
All checks were successful
Build Backend / build (push) Successful in 18m57s
Build Backend / build-docker (push) Successful in 1m4s
feat(websocket): integrate WebSocket support and refactor SSE handling
- Added WebSocket support by introducing a new WSHandler and related infrastructure.
- Replaced SSE implementations with WebSocket equivalents across message and QR code handlers.
- Updated service and repository layers to utilize WebSocket for real-time messaging.
- Removed obsolete SSE hub and related code, streamlining the application for WebSocket usage.
- Enhanced router to include WebSocket endpoints for real-time communication.
2026-03-26 21:17:49 +08:00

219 lines
5.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package wire
import (
"time"
"carrot_bbs/internal/cache"
"carrot_bbs/internal/config"
"carrot_bbs/internal/model"
"carrot_bbs/internal/pkg/crypto"
"carrot_bbs/internal/pkg/email"
"carrot_bbs/internal/pkg/hook"
"carrot_bbs/internal/pkg/openai"
"carrot_bbs/internal/pkg/redis"
"carrot_bbs/internal/pkg/s3"
"carrot_bbs/internal/pkg/ws"
"carrot_bbs/internal/repository"
"carrot_bbs/internal/service"
"github.com/google/wire"
"go.uber.org/zap"
"gorm.io/gorm"
)
// InfrastructureSet 基础设施 Provider Set
var InfrastructureSet = wire.NewSet(
// 配置
ProvideConfig,
// 日志
ProvideLogger,
// 数据库
ProvideDB,
// 事务管理器
ProvideTransactionManager,
// Redis 和缓存
ProvideRedisClient,
ProvideCache,
// 钩子系统
ProvideHookManager,
ProvideBuiltinHooks,
ProvideModerationHooks,
// WebSocket Hub
ProvideWSHub,
// 外部服务客户端
ProvideOpenAIClient,
ProvideEmailClient,
ProvideS3Client,
// 消息加密器
ProvideMessageEncryptor,
)
// ProvideConfig 加载配置
func ProvideConfig() (*config.Config, error) {
return config.Load("configs/config.yaml")
}
// ProvideDB 提供数据库连接
func ProvideDB(cfg *config.Config) (*gorm.DB, error) {
return model.NewDB(&cfg.Database)
}
// ProvideRedisClient 提供 Redis 客户端
func ProvideRedisClient(cfg *config.Config) *redis.Client {
client, err := redis.New(&cfg.Redis)
if err != nil {
zap.L().Warn("Failed to connect to Redis, falling back to memory cache",
zap.Error(err),
)
return nil
}
return client
}
// ProvideCache 提供缓存实例
func ProvideCache(cfg *config.Config, redisClient *redis.Client) cache.Cache {
cache.Configure(cache.Settings{
Enabled: cfg.Cache.Enabled,
KeyPrefix: cfg.Cache.KeyPrefix,
DefaultTTL: time.Duration(cfg.Cache.DefaultTTL) * time.Second,
NullTTL: time.Duration(cfg.Cache.NullTTL) * time.Second,
JitterRatio: cfg.Cache.JitterRatio,
PostListTTL: time.Duration(cfg.Cache.Modules.PostList) * time.Second,
ConversationTTL: time.Duration(cfg.Cache.Modules.Conversation) * time.Second,
UnreadCountTTL: time.Duration(cfg.Cache.Modules.UnreadCount) * time.Second,
GroupMembersTTL: time.Duration(cfg.Cache.Modules.GroupMembers) * time.Second,
DisableFlushDB: cfg.Cache.DisableFlushDB,
})
if cfg.Cache.Local.Enabled {
layeredCache := cache.NewLayeredCache(redisClient, &cache.LayeredCacheOptions{
LocalSize: cfg.Cache.Local.Size,
LocalBuckets: cfg.Cache.Local.Buckets,
LocalDefaultTTL: time.Duration(cfg.Cache.Local.DefaultTTL) * time.Second,
KeyPrefix: cfg.Cache.KeyPrefix,
Enabled: cfg.Cache.Enabled,
})
zap.L().Info("Initialized layered cache (local LRU + Redis)")
return layeredCache
}
if redisClient == nil {
zap.L().Warn("Redis client is nil, cache will not be available")
return nil
}
cacheInstance := cache.NewRedisCache(redisClient)
zap.L().Info("Initialized Redis cache via dependency injection")
return cacheInstance
}
// ProvideHookManager 提供钩子管理器
func ProvideHookManager() *hook.Manager {
return hook.NewManager(&hook.ManagerOptions{
MaxConcurrentAsync: 100,
Enabled: true,
})
}
// ProvideBuiltinHooks 注册内置钩子
func ProvideBuiltinHooks(manager *hook.Manager) *hook.BuiltinHooks {
builtin := hook.NewBuiltinHooks(manager)
builtin.RegisterAll()
zap.L().Info("Registered builtin hooks")
return builtin
}
// ProvideModerationHooks 注册审核钩子
func ProvideModerationHooks(
manager *hook.Manager,
postAIService *service.PostAIService,
postRepo repository.PostRepository,
commentRepo repository.CommentRepository,
cfg *config.Config,
) *hook.ModerationHooks {
strictMode := cfg.OpenAI.StrictModeration
moderationHooks := hook.NewModerationHooks(
postAIService,
nil,
postRepo,
commentRepo,
strictMode,
"***",
)
moderationHooks.RegisterAll(manager)
zap.L().Info("Registered moderation hooks",
zap.Bool("strict_mode", strictMode),
)
return moderationHooks
}
// ProvideWSHub 提供 WebSocket Hub
func ProvideWSHub() *ws.Hub {
return ws.NewHub()
}
// ProvideOpenAIClient 提供 OpenAI 客户端
func ProvideOpenAIClient(cfg *config.Config) openai.Client {
return openai.NewClient(openai.ConfigFromAppConfig(&cfg.OpenAI))
}
// ProvideEmailClient 提供邮件客户端
func ProvideEmailClient(cfg *config.Config) email.Client {
return email.NewClient(email.ConfigFromAppConfig(&cfg.Email))
}
// ProvideS3Client 提供 S3 客户端
func ProvideS3Client(cfg *config.Config) (*s3.Client, error) {
return s3.New(&cfg.S3)
}
// ProvideTransactionManager 提供事务管理器
func ProvideTransactionManager(db *gorm.DB) repository.TransactionManager {
return repository.NewTransactionManager(db)
}
// ProvideMessageEncryptor 提供消息加密器
func ProvideMessageEncryptor(cfg *config.Config) error {
if !cfg.Encryption.Enabled {
zap.L().Info("Message encryption is disabled")
return nil
}
if len(cfg.Encryption.Key) != 32 {
zap.L().Error("Encryption key must be 32 bytes for AES-256",
zap.Int("actual_length", len(cfg.Encryption.Key)),
)
return nil
}
if err := crypto.InitMessageEncryptor(cfg.Encryption.Key, cfg.Encryption.KeyVersion); err != nil {
zap.L().Error("Failed to initialize message encryptor", zap.Error(err))
return err
}
zap.L().Info("Message encryption initialized successfully",
zap.Int("key_version", cfg.Encryption.KeyVersion),
)
return nil
}
// ProvideLogger 提供 Zap Logger
func ProvideLogger() *zap.Logger {
logger, err := zap.NewProduction()
if err != nil {
panic("Failed to create logger: " + err.Error())
}
// 设置为全局 logger以便在其他地方使用 zap.L()
zap.ReplaceGlobals(logger)
return logger
}