157 lines
6.5 KiB
Go
157 lines
6.5 KiB
Go
package config
|
||
|
||
import (
|
||
"log"
|
||
"net"
|
||
"os"
|
||
"strings"
|
||
"time"
|
||
|
||
ftsService "bsm/full/module/base/fts/service"
|
||
mgtService "bsm/full/module/base/mgt/service"
|
||
passportService "bsm/full/module/base/passport/service"
|
||
senderService "bsm/full/module/base/sender/service"
|
||
walletService "bsm/full/module/finance/wallet/service"
|
||
"git.apinb.com/bsm-sdk/core/conf"
|
||
"git.apinb.com/bsm-sdk/core/env"
|
||
"git.apinb.com/bsm-sdk/core/printer"
|
||
coreVars "git.apinb.com/bsm-sdk/core/vars"
|
||
)
|
||
|
||
// SrvConfig is the ecmall aggregate configuration. It only carries the
|
||
// services registered by pkgs/ecmall: the e-commerce domain (address, mall,
|
||
// market, order), the payment domain (wallet), the platform domain (ads, cms,
|
||
// feedback, fts, logs, mgt) and the base support services they rely on
|
||
// (initial, passport, sender). Modules that are not part of ecmall keep their
|
||
// own configuration in their own process.
|
||
type SrvConfig struct {
|
||
conf.Base `yaml:",inline"`
|
||
Server ServerConfig `yaml:"Server"`
|
||
Authorization AuthorizationConfig `yaml:"Authorization"`
|
||
Databases *conf.DBConf `yaml:"Databases"`
|
||
MicroService *conf.MicroServiceConf `yaml:"MicroService"`
|
||
Rpc map[string]conf.RpcConf `yaml:"Rpc"`
|
||
Apm *conf.ApmConf `yaml:"APM"`
|
||
Etcd *conf.EtcdConf `yaml:"Etcd"`
|
||
Services []string `yaml:"Services"`
|
||
Fts *ftsService.Config `yaml:"Fts"`
|
||
Mgt *mgtService.Config `yaml:"Mgt"`
|
||
Passport *passportService.Config `yaml:"Passport"`
|
||
Sender *senderService.Config `yaml:"Sender"`
|
||
Wallet *walletService.Config `yaml:"Wallet"`
|
||
}
|
||
|
||
type ListenerConfig struct {
|
||
BindIP string `yaml:"BindIP"`
|
||
Port string `yaml:"Port"`
|
||
Addr string `yaml:"-"`
|
||
}
|
||
|
||
type ServerConfig struct {
|
||
GRPC ListenerConfig `yaml:"GRPC"`
|
||
HTTP ListenerConfig `yaml:"HTTP"`
|
||
}
|
||
|
||
type AuthorizationConfig struct {
|
||
Key string `yaml:"Key"`
|
||
Expire int64 `yaml:"Expire"`
|
||
Anonymous []string `yaml:"Anonymous"`
|
||
}
|
||
|
||
var Spec SrvConfig
|
||
|
||
// publicJwtSecretKeys 是仓库中出现过的公开 JWT 密钥字面量,绝不允许被当作有效签名密钥使用:
|
||
// - CHANGE_ME_32_BYTE_JWT_SECRET_KEY:历史样例值(恰好 32 字节,能通过长度校验);
|
||
// - Cblocksmesh2022C:bsm-sdk env.NewEnv() 的内置默认值(SDK 不可改,只能在此拒绝)。
|
||
var publicJwtSecretKeys = map[string]struct{}{
|
||
"CHANGE_ME_32_BYTE_JWT_SECRET_KEY": {},
|
||
"Cblocksmesh2022C": {},
|
||
}
|
||
|
||
// publicSessionSecret 是 session cookie HMAC 密钥的公开占位值。
|
||
const publicSessionSecret = "CHANGE_ME"
|
||
|
||
func New(serviceKey string) {
|
||
conf.New(serviceKey, &Spec)
|
||
normalizeListener(&Spec.Server.GRPC)
|
||
normalizeListener(&Spec.Server.HTTP)
|
||
if Spec.Server.GRPC.Addr == Spec.Server.HTTP.Addr {
|
||
panic("gRPC and HTTP listeners must use different addresses")
|
||
}
|
||
// JWT 是全系统身份凭据,签名密钥必须由部署显式提供:优先取 BSM_JwtSecretKey 环境变量,
|
||
// 空值与公开样例值一律拒绝启动,避免默认配置直接上线后被用于伪造任意身份。
|
||
if envKey := strings.TrimSpace(os.Getenv("BSM_JwtSecretKey")); envKey != "" {
|
||
Spec.Authorization.Key = envKey
|
||
}
|
||
Spec.Authorization.Key = strings.TrimSpace(Spec.Authorization.Key)
|
||
if Spec.Authorization.Key == "" {
|
||
log.Fatalln("ERROR: JWT secret is not configured; provide it through the BSM_JwtSecretKey environment variable")
|
||
}
|
||
if _, isPublic := publicJwtSecretKeys[Spec.Authorization.Key]; isPublic {
|
||
log.Fatalln("ERROR: JWT secret is a public sample value; provide a private key through the BSM_JwtSecretKey environment variable")
|
||
}
|
||
keyLength := len(Spec.Authorization.Key)
|
||
if keyLength != 16 && keyLength != 24 && keyLength != 32 {
|
||
log.Fatalln("ERROR: JWT secret must contain 16, 24, or 32 bytes")
|
||
}
|
||
if Spec.Authorization.Expire <= 0 {
|
||
panic("Authorization.Expire must be greater than zero")
|
||
}
|
||
// session cookie 的 HMAC 密钥同样不得为空或公开占位值。
|
||
sessionSecret := strings.TrimSpace(Spec.SecretKey)
|
||
if sessionSecret == "" || sessionSecret == publicSessionSecret {
|
||
log.Fatalln("ERROR: SecretKey must not be empty or a public placeholder; provide a private secret through the BSM_SECRET_KEY environment variable")
|
||
}
|
||
env.NewEnv().JwtSecretKey = Spec.Authorization.Key
|
||
coreVars.JwtExpire = time.Duration(Spec.Authorization.Expire) * time.Second
|
||
// Keep the embedded base address meaningful for module configurations that
|
||
// still consume it, while ecmall itself uses the two explicit listeners.
|
||
Spec.BindIP, Spec.Port, Spec.Addr = Spec.Server.HTTP.BindIP, Spec.Server.HTTP.Port, Spec.Server.HTTP.Addr
|
||
conf.NotNil(Spec.Service, Spec.Cache)
|
||
assignSharedConfig()
|
||
conf.PrintInfo(Spec.Server.GRPC.Addr)
|
||
printer.Info("gRPC Address: %s", Spec.Server.GRPC.Addr)
|
||
printer.Info("HTTP Address: %s", Spec.Server.HTTP.Addr)
|
||
}
|
||
|
||
func normalizeListener(listener *ListenerConfig) {
|
||
listener.Port = conf.CheckPort(listener.Port)
|
||
listener.BindIP = conf.CheckIP(listener.BindIP)
|
||
listener.Addr = net.JoinHostPort(listener.BindIP, listener.Port)
|
||
}
|
||
|
||
func assignSharedConfig() {
|
||
if Spec.Fts != nil {
|
||
Spec.Fts.Base, Spec.Fts.Databases, Spec.Fts.Rpc, Spec.Fts.Apm, Spec.Fts.Etcd = Spec.Base, Spec.Databases, Spec.Rpc, Spec.Apm, Spec.Etcd
|
||
}
|
||
if Spec.Mgt != nil {
|
||
Spec.Mgt.Base, Spec.Mgt.Databases, Spec.Mgt.Rpc, Spec.Mgt.Apm, Spec.Mgt.Etcd = Spec.Base, Spec.Databases, Spec.Rpc, Spec.Apm, Spec.Etcd
|
||
}
|
||
if Spec.Passport != nil {
|
||
Spec.Passport.Base, Spec.Passport.Databases, Spec.Passport.MicroService, Spec.Passport.Rpc, Spec.Passport.Apm, Spec.Passport.Etcd = Spec.Base, Spec.Databases, Spec.MicroService, Spec.Rpc, Spec.Apm, Spec.Etcd
|
||
}
|
||
if Spec.Sender != nil {
|
||
Spec.Sender.Base, Spec.Sender.Databases, Spec.Sender.MicroService, Spec.Sender.Rpc, Spec.Sender.Apm, Spec.Sender.Etcd = Spec.Base, Spec.Databases, Spec.MicroService, Spec.Rpc, Spec.Apm, Spec.Etcd
|
||
}
|
||
if Spec.Wallet != nil {
|
||
Spec.Wallet.Base, Spec.Wallet.Databases, Spec.Wallet.MicroService, Spec.Wallet.Rpc, Spec.Wallet.Apm, Spec.Wallet.Etcd = Spec.Base, Spec.Databases, Spec.MicroService, Spec.Rpc, Spec.Apm, Spec.Etcd
|
||
}
|
||
}
|
||
|
||
func Enabled(name string) bool {
|
||
if raw := strings.TrimSpace(os.Getenv("BSM_SERVICES")); raw != "" {
|
||
for _, selected := range strings.Split(raw, ",") {
|
||
if strings.EqualFold(strings.TrimSpace(selected), name) || strings.EqualFold(strings.TrimSpace(selected), "all") {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
for _, service := range Spec.Services {
|
||
if strings.EqualFold(strings.TrimSpace(service), name) || strings.EqualFold(strings.TrimSpace(service), "all") {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|