290 lines
12 KiB
Go
290 lines
12 KiB
Go
package config
|
||
|
||
import (
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
"hyapp/pkg/configx"
|
||
"hyapp/pkg/logx"
|
||
"hyapp/pkg/tencentrtc"
|
||
)
|
||
|
||
// Config 描述 gateway-service 启动所需的最小配置。
|
||
type Config struct {
|
||
ServiceName string `yaml:"service_name"`
|
||
NodeID string `yaml:"node_id"`
|
||
Environment string `yaml:"environment"`
|
||
HTTPAddr string `yaml:"http_addr"`
|
||
JWTSecret string `yaml:"jwt_secret"`
|
||
RoomServiceAddr string `yaml:"room_service_addr"`
|
||
UserServiceAddr string `yaml:"user_service_addr"`
|
||
WalletServiceAddr string `yaml:"wallet_service_addr"`
|
||
ActivityServiceAddr string `yaml:"activity_service_addr"`
|
||
GameServiceAddr string `yaml:"game_service_addr"`
|
||
AuthRateLimit AuthRateLimitConfig `yaml:"auth_rate_limit"`
|
||
LoginRisk LoginRiskConfig `yaml:"login_risk"`
|
||
AppConfig AppConfigConfig `yaml:"app_config"`
|
||
TencentIM TencentIMConfig `yaml:"tencent_im"`
|
||
TencentRTC TencentRTCConfig `yaml:"tencent_rtc"`
|
||
TencentCOS TencentCOSConfig `yaml:"tencent-cos"`
|
||
Log logx.Config `yaml:"log"`
|
||
}
|
||
|
||
// LoginRiskConfig 控制 gateway 登录入口快拦和 Redis 风控缓存读取。
|
||
type LoginRiskConfig 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"`
|
||
FastGuard LoginRiskFastGuardConfig `yaml:"fast_guard"`
|
||
}
|
||
|
||
// LoginRiskFastGuardConfig 保存语言和时区弱信号阻断配置。
|
||
type LoginRiskFastGuardConfig struct {
|
||
BlockedLanguages []string `yaml:"blocked_languages"`
|
||
BlockedLanguagePrimaryTags []string `yaml:"blocked_language_primary_tags"`
|
||
BlockedTimezones []string `yaml:"blocked_timezones"`
|
||
BlockedTimezonePrefixes []string `yaml:"blocked_timezone_prefixes"`
|
||
}
|
||
|
||
// AppConfigConfig 描述 gateway 读取后台 App 运行时配置的只读数据源。
|
||
type AppConfigConfig struct {
|
||
// MySQLDSN 指向后台管理库 hyapp_admin;gateway 只读 admin_app_configs。
|
||
MySQLDSN string `yaml:"mysql_dsn"`
|
||
}
|
||
|
||
// AuthRateLimitConfig 控制 gateway public auth 入口的 Redis 频控。
|
||
type AuthRateLimitConfig struct {
|
||
// Enabled 为 false 时关闭 gateway auth 入口频控。
|
||
Enabled bool `yaml:"enabled"`
|
||
// RedisAddr 是 public auth 频控 Redis 地址。
|
||
RedisAddr string `yaml:"redis_addr"`
|
||
// RedisPassword 是 Redis 鉴权密码,本地默认留空。
|
||
RedisPassword string `yaml:"redis_password"`
|
||
// RedisDB 是 Redis logical DB。
|
||
RedisDB int `yaml:"redis_db"`
|
||
// KeyPrefix 是频控 key 前缀,用于区分环境和服务。
|
||
KeyPrefix string `yaml:"key_prefix"`
|
||
// WindowSec 是固定窗口秒数。
|
||
WindowSec int64 `yaml:"window_sec"`
|
||
// IPLimit 限制同一入口 IP 对所有 public auth API 的请求数。
|
||
IPLimit int `yaml:"ip_limit"`
|
||
// DeviceIDLimit 限制同一 device_id 对登录、注册和 refresh 的请求数。
|
||
DeviceIDLimit int `yaml:"device_id_limit"`
|
||
// ProviderIPLimit 限制同一 IP 对同一三方 provider 的注册/登录请求数。
|
||
ProviderIPLimit int `yaml:"provider_ip_limit"`
|
||
// DisplayUserIDIPLimit 限制同一 IP 对同一 display_user_id 的密码尝试。
|
||
DisplayUserIDIPLimit int `yaml:"display_user_id_ip_limit"`
|
||
// DisplayUserIDLimit 限制同一 display_user_id 被跨 IP 喷射尝试。
|
||
DisplayUserIDLimit int `yaml:"display_user_id_limit"`
|
||
// SessionIDIPLimit 限制同一 IP 对同一 session_id 的 logout 请求数。
|
||
SessionIDIPLimit int `yaml:"session_id_ip_limit"`
|
||
}
|
||
|
||
// TencentIMConfig 描述 gateway 给客户端签发腾讯云 IM UserSig 的配置。
|
||
type TencentIMConfig struct {
|
||
// SDKAppID 是腾讯云 IM 控制台分配的应用 ID。
|
||
SDKAppID int64 `yaml:"sdk_app_id"`
|
||
// SecretKey 只允许出现在服务端配置中,不能下发给客户端。
|
||
SecretKey string `yaml:"secret_key"`
|
||
// UserSigTTL 是客户端 IM 登录票据有效期。
|
||
UserSigTTL time.Duration `yaml:"user_sig_ttl"`
|
||
// AdminIdentifier 是服务端 REST 发播报和系统消息的管理员账号,回调用它识别服务端发送者。
|
||
AdminIdentifier string `yaml:"admin_identifier"`
|
||
// CallbackURL 是在腾讯云 IM 控制台配置的服务端回调地址。
|
||
CallbackURL string `yaml:"callback_url"`
|
||
// CallbackAuthToken 是腾讯云 IM 回调鉴权 token,只用于校验回调来源。
|
||
CallbackAuthToken string `yaml:"callback_auth_token"`
|
||
}
|
||
|
||
// TencentRTCConfig 描述 gateway 给客户端签发腾讯 RTC 进房 UserSig 的配置。
|
||
type TencentRTCConfig struct {
|
||
// Enabled 控制 RTC token endpoint 是否可以对外签发,配置缺失时必须 fail-closed。
|
||
Enabled bool `yaml:"enabled"`
|
||
// SDKAppID 是腾讯 RTC 控制台分配的应用 ID。
|
||
SDKAppID int64 `yaml:"sdk_app_id"`
|
||
// SecretKey 只允许出现在服务端配置中,不能下发给客户端。
|
||
SecretKey string `yaml:"secret_key"`
|
||
// UserSigTTL 是客户端 RTC 进房票据有效期。
|
||
UserSigTTL time.Duration `yaml:"user_sig_ttl"`
|
||
// RoomIDType 首版固定 string,确保客户端只使用 TRTC strRoomId。
|
||
RoomIDType string `yaml:"room_id_type"`
|
||
// AppScene 首版固定 voice_chat_room,所有端必须一致。
|
||
AppScene string `yaml:"app_scene"`
|
||
// CallbackURL 是在腾讯 RTC 控制台配置的事件回调地址。
|
||
CallbackURL string `yaml:"callback_url"`
|
||
// CallbackSignKey 是腾讯 RTC 事件回调签名 key,只用于校验回调来源。
|
||
CallbackSignKey string `yaml:"callback_sign_key"`
|
||
}
|
||
|
||
// TencentCOSConfig 描述 gateway 代 App 上传文件到腾讯云 COS 所需的服务端配置。
|
||
type TencentCOSConfig struct {
|
||
// Enabled 控制上传接口是否真正写 COS;关闭时接口会 fail-closed。
|
||
Enabled bool `yaml:"enabled"`
|
||
// SecretID/SecretKey 只允许保存在服务端配置中,不能下发给客户端。
|
||
SecretID string `yaml:"secret-id"`
|
||
SecretKey string `yaml:"secret-key"`
|
||
// BucketName 是完整 bucket 名,例如 yumi-assets-1420526837。
|
||
BucketName string `yaml:"bucket-name"`
|
||
// Region 是 COS 地域,例如 me-saudi-arabia。
|
||
Region string `yaml:"region"`
|
||
// AccessURL 是对象对外访问域名,通常是 CDN 或绑定的自定义域名。
|
||
AccessURL string `yaml:"access-url"`
|
||
}
|
||
|
||
// Default 返回本地开发默认配置。
|
||
func Default() Config {
|
||
return Config{
|
||
ServiceName: "gateway-service",
|
||
NodeID: "gateway-local",
|
||
Environment: "local",
|
||
HTTPAddr: ":13000",
|
||
JWTSecret: "gateway-dev-secret",
|
||
RoomServiceAddr: "127.0.0.1:13001",
|
||
UserServiceAddr: "127.0.0.1:13005",
|
||
WalletServiceAddr: "127.0.0.1:13004",
|
||
ActivityServiceAddr: "127.0.0.1:13006",
|
||
GameServiceAddr: "127.0.0.1:13008",
|
||
AppConfig: AppConfigConfig{
|
||
MySQLDSN: "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_admin?parseTime=true&charset=utf8mb4&loc=UTC",
|
||
},
|
||
AuthRateLimit: AuthRateLimitConfig{
|
||
Enabled: true,
|
||
RedisAddr: "127.0.0.1:13379",
|
||
KeyPrefix: "gateway:auth:rate:",
|
||
WindowSec: 60,
|
||
IPLimit: 120,
|
||
DeviceIDLimit: 30,
|
||
ProviderIPLimit: 20,
|
||
DisplayUserIDIPLimit: 10,
|
||
DisplayUserIDLimit: 50,
|
||
SessionIDIPLimit: 30,
|
||
},
|
||
LoginRisk: LoginRiskConfig{
|
||
Enabled: true,
|
||
RedisAddr: "127.0.0.1:13379",
|
||
KeyPrefix: "login_risk:ip:",
|
||
},
|
||
TencentIM: TencentIMConfig{
|
||
UserSigTTL: 24 * time.Hour,
|
||
AdminIdentifier: "administrator",
|
||
},
|
||
TencentRTC: TencentRTCConfig{
|
||
UserSigTTL: 2 * time.Hour,
|
||
RoomIDType: "string",
|
||
AppScene: "voice_chat_room",
|
||
},
|
||
Log: logx.Config{
|
||
Level: "info",
|
||
Format: "json",
|
||
MaxPayloadBytes: 2048,
|
||
},
|
||
}
|
||
}
|
||
|
||
// Load 从独立 config.yaml 读取 gateway-service 配置。
|
||
func Load(path string) (Config, error) {
|
||
cfg := Default()
|
||
if path == "" {
|
||
if err := cfg.Normalize(); err != nil {
|
||
return Config{}, err
|
||
}
|
||
|
||
return cfg, nil
|
||
}
|
||
|
||
if err := configx.LoadYAML(path, &cfg); err != nil {
|
||
return Config{}, err
|
||
}
|
||
|
||
if err := cfg.Normalize(); err != nil {
|
||
return Config{}, err
|
||
}
|
||
|
||
return cfg, nil
|
||
}
|
||
|
||
// Normalize fills policy defaults and rejects unsupported RTC room/app modes at startup.
|
||
func (cfg *Config) Normalize() error {
|
||
cfg.ServiceName = strings.TrimSpace(cfg.ServiceName)
|
||
if cfg.ServiceName == "" {
|
||
cfg.ServiceName = "gateway-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.RoomServiceAddr = strings.TrimSpace(cfg.RoomServiceAddr)
|
||
cfg.UserServiceAddr = strings.TrimSpace(cfg.UserServiceAddr)
|
||
cfg.WalletServiceAddr = strings.TrimSpace(cfg.WalletServiceAddr)
|
||
cfg.ActivityServiceAddr = strings.TrimSpace(cfg.ActivityServiceAddr)
|
||
cfg.GameServiceAddr = strings.TrimSpace(cfg.GameServiceAddr)
|
||
if cfg.WalletServiceAddr == "" {
|
||
cfg.WalletServiceAddr = "127.0.0.1:13004"
|
||
}
|
||
if cfg.ActivityServiceAddr == "" {
|
||
cfg.ActivityServiceAddr = "127.0.0.1:13006"
|
||
}
|
||
if cfg.GameServiceAddr == "" {
|
||
cfg.GameServiceAddr = "127.0.0.1:13008"
|
||
}
|
||
cfg.AppConfig.MySQLDSN = strings.TrimSpace(cfg.AppConfig.MySQLDSN)
|
||
cfg.LoginRisk.RedisAddr = strings.TrimSpace(cfg.LoginRisk.RedisAddr)
|
||
cfg.LoginRisk.KeyPrefix = strings.TrimSpace(cfg.LoginRisk.KeyPrefix)
|
||
if cfg.LoginRisk.Enabled && cfg.LoginRisk.RedisAddr == "" {
|
||
cfg.LoginRisk.RedisAddr = strings.TrimSpace(cfg.AuthRateLimit.RedisAddr)
|
||
cfg.LoginRisk.RedisPassword = cfg.AuthRateLimit.RedisPassword
|
||
cfg.LoginRisk.RedisDB = cfg.AuthRateLimit.RedisDB
|
||
}
|
||
if cfg.LoginRisk.KeyPrefix == "" {
|
||
cfg.LoginRisk.KeyPrefix = "login_risk:ip:"
|
||
}
|
||
cfg.TencentCOS.SecretID = strings.TrimSpace(cfg.TencentCOS.SecretID)
|
||
cfg.TencentCOS.SecretKey = strings.TrimSpace(cfg.TencentCOS.SecretKey)
|
||
cfg.TencentCOS.BucketName = strings.TrimSpace(cfg.TencentCOS.BucketName)
|
||
cfg.TencentCOS.Region = strings.TrimSpace(cfg.TencentCOS.Region)
|
||
cfg.TencentCOS.AccessURL = strings.TrimRight(strings.TrimSpace(cfg.TencentCOS.AccessURL), "/")
|
||
if cfg.TencentCOS.Enabled {
|
||
if cfg.TencentCOS.SecretID == "" || cfg.TencentCOS.SecretKey == "" || cfg.TencentCOS.BucketName == "" || cfg.TencentCOS.Region == "" || cfg.TencentCOS.AccessURL == "" {
|
||
return fmt.Errorf("tencent-cos config is incomplete")
|
||
}
|
||
if !strings.HasPrefix(cfg.TencentCOS.AccessURL, "http://") && !strings.HasPrefix(cfg.TencentCOS.AccessURL, "https://") {
|
||
return fmt.Errorf("tencent-cos.access-url must be an absolute url")
|
||
}
|
||
}
|
||
|
||
cfg.TencentRTC.RoomIDType = strings.TrimSpace(cfg.TencentRTC.RoomIDType)
|
||
if cfg.TencentRTC.RoomIDType == "" {
|
||
cfg.TencentRTC.RoomIDType = tencentrtc.RoomIDTypeString
|
||
}
|
||
if cfg.TencentRTC.RoomIDType != tencentrtc.RoomIDTypeString {
|
||
// Numeric TRTC roomId would fork users into a different room than internal string room_id.
|
||
return fmt.Errorf("tencent_rtc.room_id_type must be %q", tencentrtc.RoomIDTypeString)
|
||
}
|
||
|
||
cfg.TencentRTC.AppScene = strings.TrimSpace(cfg.TencentRTC.AppScene)
|
||
if cfg.TencentRTC.AppScene == "" {
|
||
cfg.TencentRTC.AppScene = tencentrtc.AppSceneVoiceChatRoom
|
||
}
|
||
if cfg.TencentRTC.AppScene != tencentrtc.AppSceneVoiceChatRoom {
|
||
// v1 has no gateway policy surface for multiple RTC scenes, so reject drift early.
|
||
return fmt.Errorf("tencent_rtc.app_scene must be %q", tencentrtc.AppSceneVoiceChatRoom)
|
||
}
|
||
cfg.TencentRTC.CallbackURL = strings.TrimSpace(cfg.TencentRTC.CallbackURL)
|
||
cfg.TencentRTC.CallbackSignKey = strings.TrimSpace(cfg.TencentRTC.CallbackSignKey)
|
||
cfg.TencentIM.AdminIdentifier = strings.TrimSpace(cfg.TencentIM.AdminIdentifier)
|
||
if cfg.TencentIM.AdminIdentifier == "" {
|
||
cfg.TencentIM.AdminIdentifier = "administrator"
|
||
}
|
||
cfg.TencentIM.CallbackURL = strings.TrimSpace(cfg.TencentIM.CallbackURL)
|
||
cfg.TencentIM.CallbackAuthToken = strings.TrimSpace(cfg.TencentIM.CallbackAuthToken)
|
||
|
||
return nil
|
||
}
|