492 lines
20 KiB
Go
492 lines
20 KiB
Go
package config
|
||
|
||
import (
|
||
"fmt"
|
||
"net/http"
|
||
"strings"
|
||
"time"
|
||
|
||
"hyapp/pkg/configx"
|
||
"hyapp/pkg/grpcclient"
|
||
"hyapp/pkg/logx"
|
||
"hyapp/pkg/tencentim"
|
||
"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"`
|
||
CORS CORSConfig `yaml:"cors"`
|
||
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"`
|
||
GRPCClient GRPCClientConfig `yaml:"grpc_client"`
|
||
AuthRateLimit AuthRateLimitConfig `yaml:"auth_rate_limit"`
|
||
LoginRisk LoginRiskConfig `yaml:"login_risk"`
|
||
AppConfig AppConfigConfig `yaml:"app_config"`
|
||
Leaderboard LeaderboardConfig `yaml:"leaderboard"`
|
||
TencentIM TencentIMConfig `yaml:"tencent_im"`
|
||
TencentRTC TencentRTCConfig `yaml:"tencent_rtc"`
|
||
TencentCOS TencentCOSConfig `yaml:"tencent-cos"`
|
||
Log logx.Config `yaml:"log"`
|
||
}
|
||
|
||
// CORSConfig 控制浏览器/H5 访问 gateway API 的跨域入口策略。
|
||
type CORSConfig struct {
|
||
// Enabled 为 true 时,gateway 在最外层处理浏览器 preflight,避免 OPTIONS 进入业务 handler。
|
||
Enabled bool `yaml:"enabled"`
|
||
// AllowedOrigins 是允许携带凭据访问 API 的 Origin 白名单;生产不要配置裸星号。
|
||
AllowedOrigins []string `yaml:"allowed_origins"`
|
||
// AllowedMethods 是 preflight 允许的方法集合。
|
||
AllowedMethods []string `yaml:"allowed_methods"`
|
||
// AllowedHeaders 是 preflight 允许的请求头集合。
|
||
AllowedHeaders []string `yaml:"allowed_headers"`
|
||
// ExposeHeaders 是实际响应暴露给浏览器读取的响应头集合。
|
||
ExposeHeaders []string `yaml:"expose_headers"`
|
||
// AllowCredentials 控制浏览器是否允许带 cookie/Authorization 等凭据。
|
||
AllowCredentials bool `yaml:"allow_credentials"`
|
||
// MaxAgeSec 是浏览器缓存 preflight 结果的秒数。
|
||
MaxAgeSec int64 `yaml:"max_age_sec"`
|
||
}
|
||
|
||
// GRPCClientConfig 控制 gateway 访问内部服务的 gRPC transport 策略。
|
||
type GRPCClientConfig struct {
|
||
// DefaultTimeout 是没有显式 request deadline 时的 RPC 总预算,包含重试耗时。
|
||
DefaultTimeout time.Duration `yaml:"default_timeout"`
|
||
// ConnectTimeout 是单次建连最小预算,避免内网目标不可达时长期卡住。
|
||
ConnectTimeout time.Duration `yaml:"connect_timeout"`
|
||
// KeepaliveTime 是空闲连接探活周期,帮助 CLB/NAT 后的半开连接尽早发现。
|
||
KeepaliveTime time.Duration `yaml:"keepalive_time"`
|
||
// KeepaliveTimeout 是 keepalive ack 等待时间。
|
||
KeepaliveTimeout time.Duration `yaml:"keepalive_timeout"`
|
||
// RetryMaxAttempts 是包含首次调用在内的最大尝试次数;小于 2 时关闭显式重试。
|
||
RetryMaxAttempts int `yaml:"retry_max_attempts"`
|
||
// RetryInitialBackoff 是第一次重试前等待时间。
|
||
RetryInitialBackoff time.Duration `yaml:"retry_initial_backoff"`
|
||
// RetryMaxBackoff 是重试退避上限。
|
||
RetryMaxBackoff time.Duration `yaml:"retry_max_backoff"`
|
||
// RetryBackoffMultiplier 控制退避增长倍率。
|
||
RetryBackoffMultiplier float64 `yaml:"retry_backoff_multiplier"`
|
||
// RetryableStatusCodes 限定可重试状态,生产默认只允许 UNAVAILABLE。
|
||
RetryableStatusCodes []string `yaml:"retryable_status_codes"`
|
||
}
|
||
|
||
// Runtime 转换为共享 gRPC client 配置,避免 app 层重复维护默认值。
|
||
func (c GRPCClientConfig) Runtime() grpcclient.Config {
|
||
return grpcclient.Normalize(grpcclient.Config{
|
||
DefaultTimeout: c.DefaultTimeout,
|
||
ConnectTimeout: c.ConnectTimeout,
|
||
KeepaliveTime: c.KeepaliveTime,
|
||
KeepaliveTimeout: c.KeepaliveTimeout,
|
||
RetryMaxAttempts: c.RetryMaxAttempts,
|
||
RetryInitialBackoff: c.RetryInitialBackoff,
|
||
RetryMaxBackoff: c.RetryMaxBackoff,
|
||
RetryBackoffMultiplier: c.RetryBackoffMultiplier,
|
||
RetryableStatusCodes: c.RetryableStatusCodes,
|
||
})
|
||
}
|
||
|
||
// 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 只读 App 运行时配置表。
|
||
MySQLDSN string `yaml:"mysql_dsn"`
|
||
}
|
||
|
||
// LeaderboardConfig 描述 App 活动榜单读取钱包送礼事实的只读数据源。
|
||
type LeaderboardConfig struct {
|
||
// WalletMySQLDSN 指向钱包库 hyapp_wallet;只读取 wallet_transactions 的 gift_debit 事实。
|
||
WalletMySQLDSN string `yaml:"wallet_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"`
|
||
// AdminUserSigTTL 控制 gateway 补偿导入 IM 账号时的 admin UserSig 有效期。
|
||
AdminUserSigTTL time.Duration `yaml:"admin_user_sig_ttl"`
|
||
// Endpoint 是腾讯 IM REST 地域入口,例如 adminapisgp.im.qcloud.com。
|
||
Endpoint string `yaml:"endpoint"`
|
||
// RequestTimeout 是 gateway 调腾讯 IM REST 的请求超时。
|
||
RequestTimeout time.Duration `yaml:"request_timeout"`
|
||
// CallbackURL 是在腾讯云 IM 控制台配置的服务端回调地址。
|
||
CallbackURL string `yaml:"callback_url"`
|
||
// CallbackAuthToken 是腾讯云 IM 回调鉴权 token,只用于校验回调来源。
|
||
CallbackAuthToken string `yaml:"callback_auth_token"`
|
||
}
|
||
|
||
// RESTConfig 转换成腾讯 IM REST client 配置,用于 /im/usersig 入口补偿导入账号。
|
||
func (c TencentIMConfig) RESTConfig() tencentim.RESTConfig {
|
||
timeout := c.RequestTimeout
|
||
if timeout <= 0 {
|
||
timeout = 5 * time.Second
|
||
}
|
||
ttl := c.AdminUserSigTTL
|
||
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},
|
||
}
|
||
}
|
||
|
||
// 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",
|
||
CORS: CORSConfig{
|
||
Enabled: true,
|
||
AllowedOrigins: []string{
|
||
"http://localhost:3000",
|
||
"http://127.0.0.1:3000",
|
||
"http://localhost:5173",
|
||
"http://127.0.0.1:5173",
|
||
"https://global-interaction.com",
|
||
"https://www.global-interaction.com",
|
||
"https://h5.global-interaction.com",
|
||
"https://api.global-interaction.com",
|
||
},
|
||
AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
|
||
AllowedHeaders: []string{
|
||
"Authorization",
|
||
"Content-Type",
|
||
"X-Request-ID",
|
||
"X-App-Code",
|
||
"X-HY-App-Code",
|
||
"X-App-Package",
|
||
"X-Package-Name",
|
||
"X-App-Bundle-ID",
|
||
"X-Bundle-ID",
|
||
"X-App-Platform",
|
||
"X-Platform",
|
||
},
|
||
ExposeHeaders: []string{"X-Request-ID"},
|
||
AllowCredentials: true,
|
||
MaxAgeSec: 600,
|
||
},
|
||
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",
|
||
GRPCClient: GRPCClientConfig{
|
||
DefaultTimeout: 5 * time.Second,
|
||
ConnectTimeout: 2 * time.Second,
|
||
KeepaliveTime: 30 * time.Second,
|
||
KeepaliveTimeout: 10 * time.Second,
|
||
RetryMaxAttempts: 2,
|
||
RetryInitialBackoff: 100 * time.Millisecond,
|
||
RetryMaxBackoff: 500 * time.Millisecond,
|
||
RetryBackoffMultiplier: 2,
|
||
RetryableStatusCodes: []string{"UNAVAILABLE"},
|
||
},
|
||
AppConfig: AppConfigConfig{
|
||
MySQLDSN: "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_admin?parseTime=true&charset=utf8mb4&loc=UTC",
|
||
},
|
||
Leaderboard: LeaderboardConfig{
|
||
WalletMySQLDSN: "hyapp:hyapp@tcp(127.0.0.1:23306)/hyapp_wallet?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,
|
||
AdminUserSigTTL: 24 * time.Hour,
|
||
AdminIdentifier: "administrator",
|
||
RequestTimeout: 5 * time.Second,
|
||
Endpoint: tencentim.DefaultEndpoint,
|
||
},
|
||
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.CORS.AllowedOrigins = trimNonEmpty(cfg.CORS.AllowedOrigins)
|
||
cfg.CORS.AllowedMethods = upperNonEmpty(cfg.CORS.AllowedMethods)
|
||
cfg.CORS.AllowedHeaders = trimNonEmpty(cfg.CORS.AllowedHeaders)
|
||
cfg.CORS.ExposeHeaders = trimNonEmpty(cfg.CORS.ExposeHeaders)
|
||
if len(cfg.CORS.AllowedMethods) == 0 {
|
||
cfg.CORS.AllowedMethods = []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"}
|
||
}
|
||
if len(cfg.CORS.AllowedHeaders) == 0 {
|
||
cfg.CORS.AllowedHeaders = []string{"Authorization", "Content-Type", "X-Request-ID", "X-App-Code", "X-HY-App-Code", "X-App-Package", "X-Package-Name", "X-App-Bundle-ID", "X-Bundle-ID", "X-App-Platform", "X-Platform"}
|
||
}
|
||
if len(cfg.CORS.ExposeHeaders) == 0 {
|
||
cfg.CORS.ExposeHeaders = []string{"X-Request-ID"}
|
||
}
|
||
if cfg.CORS.MaxAgeSec <= 0 {
|
||
cfg.CORS.MaxAgeSec = 600
|
||
}
|
||
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"
|
||
}
|
||
runtimeGRPC := cfg.GRPCClient.Runtime()
|
||
cfg.GRPCClient = GRPCClientConfig{
|
||
DefaultTimeout: runtimeGRPC.DefaultTimeout,
|
||
ConnectTimeout: runtimeGRPC.ConnectTimeout,
|
||
KeepaliveTime: runtimeGRPC.KeepaliveTime,
|
||
KeepaliveTimeout: runtimeGRPC.KeepaliveTimeout,
|
||
RetryMaxAttempts: runtimeGRPC.RetryMaxAttempts,
|
||
RetryInitialBackoff: runtimeGRPC.RetryInitialBackoff,
|
||
RetryMaxBackoff: runtimeGRPC.RetryMaxBackoff,
|
||
RetryBackoffMultiplier: runtimeGRPC.RetryBackoffMultiplier,
|
||
RetryableStatusCodes: runtimeGRPC.RetryableStatusCodes,
|
||
}
|
||
cfg.AppConfig.MySQLDSN = strings.TrimSpace(cfg.AppConfig.MySQLDSN)
|
||
cfg.Leaderboard.WalletMySQLDSN = strings.TrimSpace(cfg.Leaderboard.WalletMySQLDSN)
|
||
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.Endpoint = strings.TrimSpace(cfg.TencentIM.Endpoint)
|
||
if cfg.TencentIM.Endpoint == "" {
|
||
cfg.TencentIM.Endpoint = tencentim.DefaultEndpoint
|
||
}
|
||
if cfg.TencentIM.AdminUserSigTTL <= 0 {
|
||
cfg.TencentIM.AdminUserSigTTL = 24 * time.Hour
|
||
}
|
||
if cfg.TencentIM.RequestTimeout <= 0 {
|
||
cfg.TencentIM.RequestTimeout = 5 * time.Second
|
||
}
|
||
cfg.TencentIM.CallbackURL = strings.TrimSpace(cfg.TencentIM.CallbackURL)
|
||
cfg.TencentIM.CallbackAuthToken = strings.TrimSpace(cfg.TencentIM.CallbackAuthToken)
|
||
|
||
return nil
|
||
}
|
||
|
||
func trimNonEmpty(values []string) []string {
|
||
out := make([]string, 0, len(values))
|
||
for _, value := range values {
|
||
value = strings.TrimSpace(value)
|
||
if value != "" {
|
||
out = append(out, value)
|
||
}
|
||
}
|
||
return out
|
||
}
|
||
|
||
func upperNonEmpty(values []string) []string {
|
||
out := make([]string, 0, len(values))
|
||
for _, value := range values {
|
||
value = strings.ToUpper(strings.TrimSpace(value))
|
||
if value != "" {
|
||
out = append(out, value)
|
||
}
|
||
}
|
||
return out
|
||
}
|