586 lines
23 KiB
Go
586 lines
23 KiB
Go
// Package config 定义 user-service 的启动配置和 YAML 加载入口。
|
||
package config
|
||
|
||
import (
|
||
"errors"
|
||
"net/http"
|
||
"strings"
|
||
"time"
|
||
|
||
"hyapp/pkg/appcode"
|
||
"hyapp/pkg/configx"
|
||
"hyapp/pkg/logx"
|
||
"hyapp/pkg/tencentim"
|
||
)
|
||
|
||
// Config 描述 user-service 启动配置。
|
||
type Config struct {
|
||
// ServiceName 是进程服务名,当前主要用于配置可读性。
|
||
ServiceName string `yaml:"service_name"`
|
||
// NodeID 是当前服务节点标识,预留给日志和后续健康响应使用。
|
||
NodeID string `yaml:"node_id"`
|
||
// Environment 是日志和部署策略使用的环境标签。
|
||
Environment string `yaml:"environment"`
|
||
// GRPCAddr 是 user-service 内部 gRPC 监听地址。
|
||
GRPCAddr string `yaml:"grpc_addr"`
|
||
// HealthHTTPAddr 是给 CLB/发布脚本探活使用的 HTTP 监听地址,不承载业务流量。
|
||
HealthHTTPAddr string `yaml:"health_http_addr"`
|
||
// MySQLDSN 是用户主数据、认证身份、session 和短号事务的必需存储连接串。
|
||
MySQLDSN string `yaml:"mysql_dsn"`
|
||
// MySQLAutoMigrate 保留给显式本地迁移;线上 schema 由发布流程或 initdb 管理。
|
||
MySQLAutoMigrate bool `yaml:"mysql_auto_migrate"`
|
||
// IDGenerator 控制 user_id 发号节点。
|
||
IDGenerator IDGeneratorConfig `yaml:"id_generator"`
|
||
// DisplayUserID 控制短号分配重试和默认短号修改冷却期。
|
||
DisplayUserID DisplayUserIDConfig `yaml:"display_user_id"`
|
||
// ThirdParty 控制本地三方登录 provider allowlist。
|
||
ThirdParty ThirdPartyConfig `yaml:"third_party"`
|
||
// InviteRechargeWorker 控制邀请有效人数的 wallet recharge outbox 消费。
|
||
InviteRechargeWorker InviteRechargeWorkerConfig `yaml:"invite_recharge_worker"`
|
||
// MicTimeWorker 控制 room-service 麦位事件消费和用户麦上时长聚合。
|
||
MicTimeWorker MicTimeWorkerConfig `yaml:"mic_time_worker"`
|
||
// CPRelationshipWorker 控制 room-service 送礼事件消费和 CP 关系申请/亲密值维护。
|
||
CPRelationshipWorker CPRelationshipWorkerConfig `yaml:"cp_relationship_worker"`
|
||
// CPIntimacyLeaderboard 控制 CP 亲密值排行榜 Redis 读模型。
|
||
CPIntimacyLeaderboard CPIntimacyLeaderboardConfig `yaml:"cp_intimacy_leaderboard"`
|
||
// OutboxWorker 控制 user_outbox 到 MQ 的补偿投递。
|
||
OutboxWorker OutboxWorkerConfig `yaml:"outbox_worker"`
|
||
// RocketMQ 控制 wallet/room outbox MQ 消费。
|
||
RocketMQ RocketMQConfig `yaml:"rocketmq"`
|
||
// LoginRisk 控制登录后 IP 风控策略、Redis session denylist 和 IP 查询 provider。
|
||
LoginRisk LoginRiskConfig `yaml:"login_risk"`
|
||
// RoomService 是封禁后驱逐当前房间用户的内部 gRPC 依赖。
|
||
RoomService RoomServiceConfig `yaml:"room_service"`
|
||
// ActivityService 是注册成功后发放注册奖励的内部 gRPC 依赖。
|
||
ActivityService ActivityServiceConfig `yaml:"activity_service"`
|
||
// WalletService 是 CP 榜头像框等资源读模型的内部 gRPC 依赖。
|
||
WalletService WalletServiceConfig `yaml:"wallet_service"`
|
||
// TencentIM 是封禁后失效用户 IM 登录态的外部集成配置。
|
||
TencentIM TencentIMConfig `yaml:"tencent_im"`
|
||
// JWT 控制 access token 和 refresh session 的签发参数。
|
||
JWT JWTConfig `yaml:"jwt"`
|
||
// Log 控制 stdout 结构化日志格式、等级和 access body 策略。
|
||
Log logx.Config `yaml:"log"`
|
||
}
|
||
|
||
// LoginRiskConfig 保存登录地区风控的 Redis、TTL 和 provider 顺序;后台扫描由 cron-service 调度。
|
||
type LoginRiskConfig struct {
|
||
Enabled bool `yaml:"enabled"`
|
||
RedisAddr string `yaml:"redis_addr"`
|
||
RedisPassword string `yaml:"redis_password"`
|
||
RedisDB int `yaml:"redis_db"`
|
||
UnsupportedCountries []string `yaml:"unsupported_countries"`
|
||
BlockedTTLSec int64 `yaml:"blocked_ttl_sec"`
|
||
AllowedTTLSec int64 `yaml:"allowed_ttl_sec"`
|
||
UnknownTTLSec int64 `yaml:"unknown_ttl_sec"`
|
||
ProviderTimeoutMs int64 `yaml:"provider_timeout_ms"`
|
||
Providers []string `yaml:"providers"`
|
||
}
|
||
|
||
// RoomServiceConfig 保存 user-service 到 room-service 的低频治理 RPC 连接参数。
|
||
type RoomServiceConfig struct {
|
||
Enabled bool `yaml:"enabled"`
|
||
Addr string `yaml:"addr"`
|
||
RequestTimeoutMs int64 `yaml:"request_timeout_ms"`
|
||
}
|
||
|
||
// ActivityServiceConfig 保存 user-service 到 activity-service 的低频注册奖励 RPC 连接参数。
|
||
type ActivityServiceConfig struct {
|
||
Enabled bool `yaml:"enabled"`
|
||
Addr string `yaml:"addr"`
|
||
RequestTimeoutMs int64 `yaml:"request_timeout_ms"`
|
||
}
|
||
|
||
// WalletServiceConfig 保存 user-service 到 wallet-service 的资源读模型 RPC 连接参数。
|
||
type WalletServiceConfig struct {
|
||
Enabled bool `yaml:"enabled"`
|
||
Addr string `yaml:"addr"`
|
||
RequestTimeoutMs int64 `yaml:"request_timeout_ms"`
|
||
}
|
||
|
||
// TencentIMConfig 描述 user-service 调用腾讯云 IM REST API 的账号级治理配置。
|
||
type TencentIMConfig struct {
|
||
Enabled bool `yaml:"enabled"`
|
||
SDKAppID int64 `yaml:"sdk_app_id"`
|
||
SecretKey string `yaml:"secret_key"`
|
||
AdminIdentifier string `yaml:"admin_identifier"`
|
||
AdminUserSigTTLSec int64 `yaml:"admin_user_sig_ttl_sec"`
|
||
Endpoint string `yaml:"endpoint"`
|
||
RequestTimeoutMs int64 `yaml:"request_timeout_ms"`
|
||
}
|
||
|
||
// RESTConfig 转换成 pkg/tencentim REST client 配置。
|
||
func (c TencentIMConfig) RESTConfig() tencentim.RESTConfig {
|
||
timeout := time.Duration(c.RequestTimeoutMs) * time.Millisecond
|
||
if timeout <= 0 {
|
||
timeout = 5 * time.Second
|
||
}
|
||
ttl := time.Duration(c.AdminUserSigTTLSec) * time.Second
|
||
if ttl <= 0 {
|
||
ttl = 24 * time.Hour
|
||
}
|
||
return tencentim.RESTConfig{
|
||
SDKAppID: c.SDKAppID,
|
||
SecretKey: c.SecretKey,
|
||
AdminIdentifier: c.AdminIdentifier,
|
||
AdminUserSigTTL: ttl,
|
||
Endpoint: c.Endpoint,
|
||
GroupType: tencentim.DefaultGroupType,
|
||
HTTPClient: &http.Client{Timeout: timeout},
|
||
}
|
||
}
|
||
|
||
// IDGeneratorConfig 保存 user_id 发号节点配置。
|
||
type IDGeneratorConfig struct {
|
||
// NodeID 参与雪花 ID 发号,多个 user-service 节点必须不同。
|
||
NodeID int64 `yaml:"node_id"`
|
||
}
|
||
|
||
// DisplayUserIDConfig 保存短号分配和修改策略。
|
||
type DisplayUserIDConfig struct {
|
||
// AllocateMaxAttempts 是默认短号冲突时的最大重试次数。
|
||
AllocateMaxAttempts int `yaml:"allocate_max_attempts"`
|
||
// ChangeCooldownSec 是默认短号修改冷却期秒数。
|
||
ChangeCooldownSec int64 `yaml:"change_cooldown_sec"`
|
||
}
|
||
|
||
// ThirdPartyConfig 保存三方登录 provider allowlist 和真实 verifier 配置。
|
||
type ThirdPartyConfig struct {
|
||
// AllowedProviders 是 provider allowlist;firebase 会被强制路由到 Firebase verifier。
|
||
AllowedProviders []string `yaml:"allowed_providers"`
|
||
// Firebase 保存 Firebase Auth ID token verifier 配置。
|
||
Firebase FirebaseConfig `yaml:"firebase"`
|
||
}
|
||
|
||
// FirebaseConfig 保存 Firebase Auth ID token verifier 配置。
|
||
type FirebaseConfig struct {
|
||
// ProjectID 必须匹配 Firebase token 的 aud 和 issuer。
|
||
ProjectID string `yaml:"project_id"`
|
||
// AllowedSignInProviders 限制 firebase.sign_in_provider,首版默认 google.com。
|
||
AllowedSignInProviders []string `yaml:"allowed_sign_in_providers"`
|
||
// CertsURL 只用于测试或私有代理;生产为空时使用 Firebase 官方公钥端点。
|
||
CertsURL string `yaml:"certs_url"`
|
||
// Projects 按 app_code 配置 Firebase 项目;Huwaa/Lalu 必须各自校验自己的 aud/iss。
|
||
Projects map[string]FirebaseProjectConfig `yaml:"projects"`
|
||
}
|
||
|
||
// FirebaseProjectConfig 保存单个 App 的 Firebase 项目配置。
|
||
type FirebaseProjectConfig struct {
|
||
// ProjectID 必须匹配该 App Firebase token 的 aud 和 issuer。
|
||
ProjectID string `yaml:"project_id"`
|
||
// AllowedSignInProviders 为空时继承 firebase.allowed_sign_in_providers。
|
||
AllowedSignInProviders []string `yaml:"allowed_sign_in_providers"`
|
||
// CertsURL 为空时继承 firebase.certs_url,生产通常不需要配置。
|
||
CertsURL string `yaml:"certs_url"`
|
||
}
|
||
|
||
// InviteRechargeWorkerConfig 保存钱包充值事实消费策略。
|
||
type InviteRechargeWorkerConfig struct {
|
||
// Enabled 控制是否启动钱包充值 MQ 消费。
|
||
Enabled bool `yaml:"enabled"`
|
||
}
|
||
|
||
// MicTimeWorkerConfig 保存房间麦位事件消费策略。
|
||
type MicTimeWorkerConfig struct {
|
||
// Enabled 控制是否启动 room outbox MQ 消费;关闭后查询仍可读取已聚合数据。
|
||
Enabled bool `yaml:"enabled"`
|
||
}
|
||
|
||
// CPRelationshipWorkerConfig 保存 CP 礼物事件消费策略。
|
||
type CPRelationshipWorkerConfig struct {
|
||
// Enabled 控制是否启动 room gift MQ 消费;关闭后只能处理历史已消费关系。
|
||
Enabled bool `yaml:"enabled"`
|
||
// ConsumerGroup 必须独立于麦位消费,避免两个读模型共享 MQ 位点。
|
||
ConsumerGroup string `yaml:"consumer_group"`
|
||
}
|
||
|
||
// CPIntimacyLeaderboardConfig 保存 CP 亲密榜 Redis zset 的连接和 key 前缀。
|
||
type CPIntimacyLeaderboardConfig struct {
|
||
Enabled bool `yaml:"enabled"`
|
||
RedisAddr string `yaml:"redis_addr"`
|
||
RedisPassword string `yaml:"redis_password"`
|
||
RedisDB int `yaml:"redis_db"`
|
||
KeyPrefix string `yaml:"key_prefix"`
|
||
}
|
||
|
||
// OutboxWorkerConfig 保存 user_outbox MQ 发布策略。
|
||
type OutboxWorkerConfig struct {
|
||
Enabled bool `yaml:"enabled"`
|
||
PollInterval time.Duration `yaml:"poll_interval"`
|
||
BatchSize int `yaml:"batch_size"`
|
||
PublishTimeout time.Duration `yaml:"publish_timeout"`
|
||
}
|
||
|
||
// RocketMQConfig 保存 user-service 消费 owner service outbox 的 MQ 连接。
|
||
type RocketMQConfig struct {
|
||
Enabled bool `yaml:"enabled"`
|
||
NameServers []string `yaml:"name_servers"`
|
||
NameServerDomain string `yaml:"name_server_domain"`
|
||
AccessKey string `yaml:"access_key"`
|
||
SecretKey string `yaml:"secret_key"`
|
||
SecurityToken string `yaml:"security_token"`
|
||
Namespace string `yaml:"namespace"`
|
||
VIPChannel bool `yaml:"vip_channel"`
|
||
WalletOutbox WalletOutboxMQConfig `yaml:"wallet_outbox"`
|
||
RoomOutbox RoomOutboxMQConfig `yaml:"room_outbox"`
|
||
UserOutbox UserOutboxMQConfig `yaml:"user_outbox"`
|
||
}
|
||
|
||
type WalletOutboxMQConfig struct {
|
||
Enabled bool `yaml:"enabled"`
|
||
Topic string `yaml:"topic"`
|
||
ConsumerGroup string `yaml:"consumer_group"`
|
||
ConsumerMaxReconsumeTimes int32 `yaml:"consumer_max_reconsume_times"`
|
||
}
|
||
|
||
type RoomOutboxMQConfig struct {
|
||
Enabled bool `yaml:"enabled"`
|
||
Topic string `yaml:"topic"`
|
||
ConsumerGroup string `yaml:"consumer_group"`
|
||
ConsumerMaxReconsumeTimes int32 `yaml:"consumer_max_reconsume_times"`
|
||
}
|
||
|
||
type UserOutboxMQConfig struct {
|
||
Enabled bool `yaml:"enabled"`
|
||
Topic string `yaml:"topic"`
|
||
ProducerGroup string `yaml:"producer_group"`
|
||
}
|
||
|
||
// JWTConfig 保存 access token 和 refresh token 的签发策略。
|
||
type JWTConfig struct {
|
||
// Issuer 写入 access token iss claim。
|
||
Issuer string `yaml:"issuer"`
|
||
// AccessTokenTTLSec 是 App 登录 access token 有效期;默认 168 小时,减少正常用户被动重登。
|
||
AccessTokenTTLSec int64 `yaml:"access_token_ttl_sec"`
|
||
// RefreshTokenTTLSec 是服务端 session/refresh token 有效期。
|
||
RefreshTokenTTLSec int64 `yaml:"refresh_token_ttl_sec"`
|
||
// SigningAlg 当前只支持 HS256。
|
||
SigningAlg string `yaml:"signing_alg"`
|
||
// SigningSecret 是 HS256 签名密钥,生产不能使用默认开发值。
|
||
SigningSecret string `yaml:"signing_secret"`
|
||
}
|
||
|
||
// Default 返回本地默认配置。
|
||
func Default() Config {
|
||
// 默认端口遵守项目 13xxx 约束;本地进程默认连接 docker-compose 暴露的 MySQL。
|
||
return Config{
|
||
ServiceName: "user-service",
|
||
NodeID: "user-local",
|
||
Environment: "local",
|
||
GRPCAddr: ":13005",
|
||
HealthHTTPAddr: ":13105",
|
||
MySQLDSN: "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_user?parseTime=true&charset=utf8mb4&loc=UTC",
|
||
MySQLAutoMigrate: false,
|
||
IDGenerator: IDGeneratorConfig{
|
||
NodeID: 1,
|
||
},
|
||
DisplayUserID: DisplayUserIDConfig{
|
||
AllocateMaxAttempts: 8,
|
||
ChangeCooldownSec: 2592000,
|
||
},
|
||
ThirdParty: ThirdPartyConfig{
|
||
AllowedProviders: []string{"firebase"},
|
||
Firebase: FirebaseConfig{
|
||
AllowedSignInProviders: []string{"google.com"},
|
||
},
|
||
},
|
||
InviteRechargeWorker: InviteRechargeWorkerConfig{
|
||
Enabled: false,
|
||
},
|
||
MicTimeWorker: MicTimeWorkerConfig{
|
||
Enabled: false,
|
||
},
|
||
CPRelationshipWorker: CPRelationshipWorkerConfig{
|
||
Enabled: false,
|
||
ConsumerGroup: "hyapp-user-cp-room-outbox",
|
||
},
|
||
CPIntimacyLeaderboard: CPIntimacyLeaderboardConfig{
|
||
Enabled: true,
|
||
RedisAddr: "127.0.0.1:13379",
|
||
KeyPrefix: "user:cp_intimacy_leaderboard",
|
||
},
|
||
OutboxWorker: OutboxWorkerConfig{
|
||
Enabled: false,
|
||
PollInterval: time.Second,
|
||
BatchSize: 100,
|
||
PublishTimeout: 3 * time.Second,
|
||
},
|
||
RocketMQ: defaultRocketMQConfig(),
|
||
LoginRisk: LoginRiskConfig{
|
||
Enabled: true,
|
||
RedisAddr: "127.0.0.1:13379",
|
||
UnsupportedCountries: []string{"CN"},
|
||
BlockedTTLSec: 2592000,
|
||
AllowedTTLSec: 604800,
|
||
UnknownTTLSec: 1800,
|
||
ProviderTimeoutMs: 800,
|
||
Providers: []string{"edge_country"},
|
||
},
|
||
RoomService: RoomServiceConfig{
|
||
Enabled: true,
|
||
Addr: "127.0.0.1:13001",
|
||
RequestTimeoutMs: 3000,
|
||
},
|
||
ActivityService: ActivityServiceConfig{
|
||
Enabled: true,
|
||
Addr: "127.0.0.1:13006",
|
||
RequestTimeoutMs: 3000,
|
||
},
|
||
WalletService: WalletServiceConfig{
|
||
Enabled: true,
|
||
Addr: "127.0.0.1:13004",
|
||
RequestTimeoutMs: 3000,
|
||
},
|
||
TencentIM: TencentIMConfig{
|
||
Enabled: false,
|
||
AdminIdentifier: "administrator",
|
||
AdminUserSigTTLSec: 86400,
|
||
Endpoint: tencentim.DefaultEndpoint,
|
||
RequestTimeoutMs: 5000,
|
||
},
|
||
JWT: JWTConfig{
|
||
Issuer: "hyapp",
|
||
AccessTokenTTLSec: 604800,
|
||
RefreshTokenTTLSec: 2592000,
|
||
SigningAlg: "HS256",
|
||
SigningSecret: "dev-shared-secret",
|
||
},
|
||
Log: logx.Config{
|
||
Level: "info",
|
||
Format: "json",
|
||
MaxPayloadBytes: 2048,
|
||
},
|
||
}
|
||
}
|
||
|
||
// Load 从独立 config.yaml 读取 user-service 配置。
|
||
func Load(path string) (Config, error) {
|
||
cfg := Default()
|
||
if path == "" {
|
||
// 空路径显式使用默认配置,便于单元测试和最小启动。
|
||
return cfg, nil
|
||
}
|
||
|
||
if err := configx.LoadYAML(path, &cfg); err != nil {
|
||
// 配置解析失败返回空 Config,避免调用方误用部分默认值继续启动。
|
||
return Config{}, err
|
||
}
|
||
|
||
cfg.ServiceName = strings.TrimSpace(cfg.ServiceName)
|
||
if cfg.ServiceName == "" {
|
||
cfg.ServiceName = "user-service"
|
||
}
|
||
cfg.NodeID = strings.TrimSpace(cfg.NodeID)
|
||
if cfg.NodeID == "" {
|
||
cfg.NodeID = cfg.ServiceName
|
||
}
|
||
cfg.Environment = strings.ToLower(strings.TrimSpace(cfg.Environment))
|
||
if cfg.Environment == "" {
|
||
cfg.Environment = "local"
|
||
}
|
||
cfg.HealthHTTPAddr = strings.TrimSpace(cfg.HealthHTTPAddr)
|
||
if cfg.HealthHTTPAddr == "" {
|
||
cfg.HealthHTTPAddr = ":13105"
|
||
}
|
||
cfg.LoginRisk.RedisAddr = strings.TrimSpace(cfg.LoginRisk.RedisAddr)
|
||
cfg.CPIntimacyLeaderboard.RedisAddr = strings.TrimSpace(cfg.CPIntimacyLeaderboard.RedisAddr)
|
||
cfg.CPIntimacyLeaderboard.KeyPrefix = strings.Trim(strings.TrimSpace(cfg.CPIntimacyLeaderboard.KeyPrefix), ":")
|
||
if cfg.CPIntimacyLeaderboard.RedisAddr == "" {
|
||
cfg.CPIntimacyLeaderboard.RedisAddr = cfg.LoginRisk.RedisAddr
|
||
}
|
||
if cfg.CPIntimacyLeaderboard.KeyPrefix == "" {
|
||
cfg.CPIntimacyLeaderboard.KeyPrefix = "user:cp_intimacy_leaderboard"
|
||
}
|
||
cfg.ThirdParty.AllowedProviders = normalizeStringSlice(cfg.ThirdParty.AllowedProviders)
|
||
cfg.ThirdParty.Firebase = normalizeFirebaseConfig(cfg.ThirdParty.Firebase)
|
||
cfg.RoomService.Addr = strings.TrimSpace(cfg.RoomService.Addr)
|
||
if cfg.RoomService.Addr == "" {
|
||
cfg.RoomService.Addr = "127.0.0.1:13001"
|
||
}
|
||
if cfg.RoomService.RequestTimeoutMs <= 0 {
|
||
cfg.RoomService.RequestTimeoutMs = 3000
|
||
}
|
||
cfg.ActivityService.Addr = strings.TrimSpace(cfg.ActivityService.Addr)
|
||
if cfg.ActivityService.Addr == "" {
|
||
cfg.ActivityService.Addr = "127.0.0.1:13006"
|
||
}
|
||
if cfg.ActivityService.RequestTimeoutMs <= 0 {
|
||
cfg.ActivityService.RequestTimeoutMs = 3000
|
||
}
|
||
cfg.WalletService.Addr = strings.TrimSpace(cfg.WalletService.Addr)
|
||
if cfg.WalletService.Addr == "" {
|
||
cfg.WalletService.Addr = "127.0.0.1:13004"
|
||
}
|
||
if cfg.WalletService.RequestTimeoutMs <= 0 {
|
||
cfg.WalletService.RequestTimeoutMs = 3000
|
||
}
|
||
cfg.TencentIM.SecretKey = strings.TrimSpace(cfg.TencentIM.SecretKey)
|
||
cfg.TencentIM.AdminIdentifier = strings.TrimSpace(cfg.TencentIM.AdminIdentifier)
|
||
if cfg.TencentIM.AdminIdentifier == "" {
|
||
cfg.TencentIM.AdminIdentifier = "administrator"
|
||
}
|
||
cfg.TencentIM.Endpoint = strings.TrimSpace(cfg.TencentIM.Endpoint)
|
||
if cfg.TencentIM.Endpoint == "" {
|
||
cfg.TencentIM.Endpoint = tencentim.DefaultEndpoint
|
||
}
|
||
if cfg.TencentIM.AdminUserSigTTLSec <= 0 {
|
||
cfg.TencentIM.AdminUserSigTTLSec = 86400
|
||
}
|
||
if cfg.TencentIM.RequestTimeoutMs <= 0 {
|
||
cfg.TencentIM.RequestTimeoutMs = 5000
|
||
}
|
||
if cfg.LoginRisk.BlockedTTLSec <= 0 {
|
||
cfg.LoginRisk.BlockedTTLSec = 2592000
|
||
}
|
||
if cfg.LoginRisk.AllowedTTLSec <= 0 {
|
||
cfg.LoginRisk.AllowedTTLSec = 604800
|
||
}
|
||
if cfg.LoginRisk.UnknownTTLSec <= 0 {
|
||
cfg.LoginRisk.UnknownTTLSec = 1800
|
||
}
|
||
if cfg.LoginRisk.ProviderTimeoutMs <= 0 {
|
||
cfg.LoginRisk.ProviderTimeoutMs = 800
|
||
}
|
||
for index, provider := range cfg.LoginRisk.Providers {
|
||
cfg.LoginRisk.Providers[index] = strings.TrimSpace(provider)
|
||
}
|
||
rocketMQ, err := normalizeRocketMQConfig(cfg.RocketMQ)
|
||
if err != nil {
|
||
return Config{}, err
|
||
}
|
||
cfg.RocketMQ = rocketMQ
|
||
if cfg.InviteRechargeWorker.Enabled && !cfg.RocketMQ.WalletOutbox.Enabled {
|
||
return Config{}, errors.New("invite_recharge_worker requires rocketmq.wallet_outbox.enabled")
|
||
}
|
||
if cfg.MicTimeWorker.Enabled && !cfg.RocketMQ.RoomOutbox.Enabled {
|
||
return Config{}, errors.New("mic_time_worker requires rocketmq.room_outbox.enabled")
|
||
}
|
||
cfg.CPRelationshipWorker.ConsumerGroup = strings.TrimSpace(cfg.CPRelationshipWorker.ConsumerGroup)
|
||
if cfg.CPRelationshipWorker.ConsumerGroup == "" {
|
||
cfg.CPRelationshipWorker.ConsumerGroup = "hyapp-user-cp-room-outbox"
|
||
}
|
||
if cfg.CPRelationshipWorker.Enabled && !cfg.RocketMQ.RoomOutbox.Enabled {
|
||
return Config{}, errors.New("cp_relationship_worker requires rocketmq.room_outbox.enabled")
|
||
}
|
||
if cfg.OutboxWorker.PollInterval <= 0 {
|
||
cfg.OutboxWorker.PollInterval = time.Second
|
||
}
|
||
if cfg.OutboxWorker.BatchSize <= 0 {
|
||
cfg.OutboxWorker.BatchSize = 100
|
||
}
|
||
if cfg.OutboxWorker.PublishTimeout <= 0 {
|
||
cfg.OutboxWorker.PublishTimeout = 3 * time.Second
|
||
}
|
||
if cfg.OutboxWorker.Enabled && !cfg.RocketMQ.UserOutbox.Enabled {
|
||
return Config{}, errors.New("outbox_worker requires rocketmq.user_outbox.enabled")
|
||
}
|
||
return cfg, nil
|
||
}
|
||
|
||
func defaultRocketMQConfig() RocketMQConfig {
|
||
return RocketMQConfig{
|
||
Enabled: false,
|
||
WalletOutbox: WalletOutboxMQConfig{
|
||
Enabled: false,
|
||
Topic: "hyapp_wallet_outbox",
|
||
ConsumerGroup: "hyapp-user-invite-wallet-outbox",
|
||
ConsumerMaxReconsumeTimes: 16,
|
||
},
|
||
RoomOutbox: RoomOutboxMQConfig{
|
||
Enabled: false,
|
||
Topic: "hyapp_room_outbox",
|
||
ConsumerGroup: "hyapp-user-mictime-room-outbox",
|
||
ConsumerMaxReconsumeTimes: 16,
|
||
},
|
||
UserOutbox: UserOutboxMQConfig{
|
||
Enabled: false,
|
||
Topic: "hyapp_user_outbox",
|
||
ProducerGroup: "hyapp-user-outbox-producer",
|
||
},
|
||
}
|
||
}
|
||
|
||
func normalizeRocketMQConfig(cfg RocketMQConfig) (RocketMQConfig, error) {
|
||
defaults := defaultRocketMQConfig()
|
||
cfg.NameServers = normalizeStringSlice(cfg.NameServers)
|
||
cfg.NameServerDomain = strings.TrimSpace(cfg.NameServerDomain)
|
||
cfg.AccessKey = strings.TrimSpace(cfg.AccessKey)
|
||
cfg.SecretKey = strings.TrimSpace(cfg.SecretKey)
|
||
cfg.SecurityToken = strings.TrimSpace(cfg.SecurityToken)
|
||
cfg.Namespace = strings.TrimSpace(cfg.Namespace)
|
||
if cfg.WalletOutbox.Topic = strings.TrimSpace(cfg.WalletOutbox.Topic); cfg.WalletOutbox.Topic == "" {
|
||
cfg.WalletOutbox.Topic = defaults.WalletOutbox.Topic
|
||
}
|
||
if cfg.WalletOutbox.ConsumerGroup = strings.TrimSpace(cfg.WalletOutbox.ConsumerGroup); cfg.WalletOutbox.ConsumerGroup == "" {
|
||
cfg.WalletOutbox.ConsumerGroup = defaults.WalletOutbox.ConsumerGroup
|
||
}
|
||
if cfg.WalletOutbox.ConsumerMaxReconsumeTimes <= 0 {
|
||
cfg.WalletOutbox.ConsumerMaxReconsumeTimes = defaults.WalletOutbox.ConsumerMaxReconsumeTimes
|
||
}
|
||
if cfg.RoomOutbox.Topic = strings.TrimSpace(cfg.RoomOutbox.Topic); cfg.RoomOutbox.Topic == "" {
|
||
cfg.RoomOutbox.Topic = defaults.RoomOutbox.Topic
|
||
}
|
||
if cfg.RoomOutbox.ConsumerGroup = strings.TrimSpace(cfg.RoomOutbox.ConsumerGroup); cfg.RoomOutbox.ConsumerGroup == "" {
|
||
cfg.RoomOutbox.ConsumerGroup = defaults.RoomOutbox.ConsumerGroup
|
||
}
|
||
if cfg.RoomOutbox.ConsumerMaxReconsumeTimes <= 0 {
|
||
cfg.RoomOutbox.ConsumerMaxReconsumeTimes = defaults.RoomOutbox.ConsumerMaxReconsumeTimes
|
||
}
|
||
if cfg.UserOutbox.Topic = strings.TrimSpace(cfg.UserOutbox.Topic); cfg.UserOutbox.Topic == "" {
|
||
cfg.UserOutbox.Topic = defaults.UserOutbox.Topic
|
||
}
|
||
if cfg.UserOutbox.ProducerGroup = strings.TrimSpace(cfg.UserOutbox.ProducerGroup); cfg.UserOutbox.ProducerGroup == "" {
|
||
cfg.UserOutbox.ProducerGroup = defaults.UserOutbox.ProducerGroup
|
||
}
|
||
if cfg.WalletOutbox.Enabled || cfg.RoomOutbox.Enabled || cfg.UserOutbox.Enabled {
|
||
cfg.Enabled = true
|
||
}
|
||
if cfg.Enabled {
|
||
if len(cfg.NameServers) == 0 && cfg.NameServerDomain == "" {
|
||
return RocketMQConfig{}, errors.New("rocketmq name_servers or name_server_domain is required")
|
||
}
|
||
if (cfg.AccessKey == "") != (cfg.SecretKey == "") {
|
||
return RocketMQConfig{}, errors.New("rocketmq access_key and secret_key must be configured together")
|
||
}
|
||
}
|
||
return cfg, nil
|
||
}
|
||
|
||
func normalizeFirebaseConfig(cfg FirebaseConfig) FirebaseConfig {
|
||
cfg.ProjectID = strings.TrimSpace(cfg.ProjectID)
|
||
cfg.AllowedSignInProviders = normalizeStringSlice(cfg.AllowedSignInProviders)
|
||
cfg.CertsURL = strings.TrimSpace(cfg.CertsURL)
|
||
if len(cfg.Projects) == 0 {
|
||
return cfg
|
||
}
|
||
|
||
projects := make(map[string]FirebaseProjectConfig, len(cfg.Projects))
|
||
for rawAppCode, project := range cfg.Projects {
|
||
app := appcode.Normalize(rawAppCode)
|
||
project.ProjectID = strings.TrimSpace(project.ProjectID)
|
||
project.CertsURL = strings.TrimSpace(project.CertsURL)
|
||
project.AllowedSignInProviders = normalizeStringSlice(project.AllowedSignInProviders)
|
||
if len(project.AllowedSignInProviders) == 0 {
|
||
// 单 App 没写登录方式时继承全局配置;全局为空时 auth verifier 仍会默认 google.com。
|
||
project.AllowedSignInProviders = cfg.AllowedSignInProviders
|
||
}
|
||
if project.CertsURL == "" {
|
||
// Firebase 公钥端点对所有项目相同;测试代理或私有代理可以只写一次。
|
||
project.CertsURL = cfg.CertsURL
|
||
}
|
||
projects[app] = project
|
||
}
|
||
cfg.Projects = projects
|
||
return cfg
|
||
}
|
||
|
||
func normalizeStringSlice(values []string) []string {
|
||
normalized := make([]string, 0, len(values))
|
||
for _, value := range values {
|
||
if value = strings.TrimSpace(value); value != "" {
|
||
normalized = append(normalized, value)
|
||
}
|
||
}
|
||
return normalized
|
||
}
|