Bladeren bron

feat(config): add telegram.sessions_dir and telegram.secret_key with env expansion

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dot 2 weken geleden
bovenliggende
commit
1355314c07
3 gewijzigde bestanden met toevoegingen van 66 en 5 verwijderingen
  1. 11 1
      configs/config.yaml
  2. 3 0
      deploy/.env.example
  3. 52 4
      internal/config/config.go

+ 11 - 1
configs/config.yaml

@@ -18,6 +18,8 @@ telegram:
   app_id: 0
   app_hash: ""
   accounts: []
+  sessions_dir: "/app/sessions"
+  secret_key: "${TG_SECRET_KEY}"   # 32-byte base64 loaded from env
 
 llm:
   provider: "openai"
@@ -27,9 +29,17 @@ llm:
   timeout: "30s"
 
 serper:
-  api_key: ""
+  api_key: "fcdf0cdc35023f7556020d3dad35c0319e9076bf"
   results_per_page: 10
   max_pages: 3
 
 github:
   token: ""
+
+security:
+  jwt_secret: ""          # leave empty to use default, set a strong secret in production
+  password_min_len: 8
+  webhook_allow_http: false
+
+app:
+  name: "商户采集系统"

+ 3 - 0
deploy/.env.example

@@ -0,0 +1,3 @@
+# 32-byte AES-GCM master key for 2FA encryption. Generate with:
+#   openssl rand -base64 32
+TG_SECRET_KEY=CHANGE_ME_TO_32_RANDOM_BASE64_BYTES_XXXXXXXXXXX=

+ 52 - 4
internal/config/config.go

@@ -2,6 +2,7 @@ package config
 
 import (
 	"fmt"
+	"os"
 
 	"github.com/spf13/viper"
 )
@@ -14,10 +15,24 @@ type Config struct {
 	LLM      LLMConfig
 	Serper   SerperConfig
 	GitHub   GitHubConfig
+	Security SecurityConfig
+	App      AppConfig
 }
 
 type ServerConfig struct {
-	Port int
+	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 {
@@ -36,9 +51,11 @@ type RedisConfig struct {
 }
 
 type TelegramConfig struct {
-	AppID    int        `mapstructure:"app_id"`
-	AppHash  string     `mapstructure:"app_hash"`
-	Accounts []TGAccount
+	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 {
@@ -80,6 +97,28 @@ func Load(path string) (*Config, error) {
 		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
 }
@@ -87,3 +126,12 @@ func Load(path string) (*Config, error) {
 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
+}