| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 |
- package config
- import (
- "fmt"
- "os"
- "github.com/spf13/viper"
- )
- type Config struct {
- Server ServerConfig
- MySQL MySQLConfig
- Redis RedisConfig
- Telegram TelegramConfig
- LLM LLMConfig
- Serper SerperConfig
- GitHub GitHubConfig
- Security SecurityConfig
- App AppConfig
- }
- type ServerConfig struct {
- Port int
- CORSOrigins []string `mapstructure:"cors_origins"` // allowed origins, empty = *
- MaxUploadMB int `mapstructure:"max_upload_mb"` // max upload size in MB, default 10
- }
- type SecurityConfig struct {
- JWTSecret string `mapstructure:"jwt_secret"`
- PasswordMinLen int `mapstructure:"password_min_len"` // default 8
- WebhookAllowHTTP bool `mapstructure:"webhook_allow_http"` // allow http:// webhooks
- }
- type AppConfig struct {
- Name string // display name, default "商户采集系统"
- }
- type MySQLConfig struct {
- Host string
- Port int
- User string
- Password string
- Database string
- }
- type RedisConfig struct {
- Host string
- Port int
- Password string
- DB int
- }
- type TelegramConfig struct {
- AppID int `mapstructure:"app_id"`
- AppHash string `mapstructure:"app_hash"`
- Accounts []TGAccount
- SessionsDir string `mapstructure:"sessions_dir"` // absolute path, e.g. /app/sessions
- SecretKey string `mapstructure:"secret_key"` // base64 32-byte, or literal ${TG_SECRET_KEY}
- }
- type TGAccount struct {
- Phone string
- SessionFile string `mapstructure:"session_file"`
- }
- type LLMConfig struct {
- Provider string
- BaseURL string `mapstructure:"base_url"`
- APIKey string `mapstructure:"api_key"`
- Model string
- Timeout string
- }
- type SerperConfig struct {
- APIKey string `mapstructure:"api_key"`
- ResultsPerPage int `mapstructure:"results_per_page"`
- MaxPages int `mapstructure:"max_pages"`
- }
- type GitHubConfig struct {
- Token string
- }
- var global *Config
- func Load(path string) (*Config, error) {
- v := viper.New()
- v.SetConfigFile(path)
- v.SetConfigType("yaml")
- if err := v.ReadInConfig(); err != nil {
- return nil, fmt.Errorf("read config file: %w", err)
- }
- cfg := &Config{}
- if err := v.Unmarshal(cfg); err != nil {
- return nil, fmt.Errorf("unmarshal config: %w", err)
- }
- // Apply defaults
- if cfg.Security.JWTSecret == "" {
- cfg.Security.JWTSecret = "spider-jwt-secret-2026"
- }
- if cfg.Security.PasswordMinLen < 6 {
- cfg.Security.PasswordMinLen = 8
- }
- if cfg.Server.MaxUploadMB <= 0 {
- cfg.Server.MaxUploadMB = 10
- }
- if cfg.App.Name == "" {
- cfg.App.Name = "商户采集系统"
- }
- // Resolve ${ENV_VAR} placeholders for secret fields.
- cfg.Telegram.SecretKey = expandEnvPlaceholder(cfg.Telegram.SecretKey)
- cfg.Security.JWTSecret = expandEnvPlaceholder(cfg.Security.JWTSecret)
- if cfg.Telegram.SessionsDir == "" {
- cfg.Telegram.SessionsDir = "/app/sessions"
- }
- global = cfg
- return cfg, nil
- }
- func Get() *Config {
- return global
- }
- // expandEnvPlaceholder replaces a value of the form "${VAR}" with os.Getenv("VAR").
- // A non-placeholder value is returned unchanged.
- func expandEnvPlaceholder(v string) string {
- if len(v) > 3 && v[0] == '$' && v[1] == '{' && v[len(v)-1] == '}' {
- return os.Getenv(v[2 : len(v)-1])
- }
- return v
- }
|