package config import ( "fmt" "os" "path/filepath" "strconv" "strings" "gopkg.in/ini.v1" ) // Config 应用全局配置 type Config struct { WorkPath string Port int DataDir string JWTSecret string DBDSN string DevMode bool } // Parse 解析配置:环境变量 > app.ini > 默认值 func Parse() (*Config, error) { workPath, err := os.Getwd() if err != nil { return nil, fmt.Errorf("获取工作目录失败: %w", err) } // 默认值 cfg := &Config{ WorkPath: workPath, Port: 3001, DataDir: filepath.Join(workPath, "data"), DBDSN: "postgres://postgres:postgres@localhost:5432/jiang13?sslmode=disable", DevMode: true, } // 尝试加载 app.ini iniPath := filepath.Join(workPath, "app.ini") if _, err := os.Stat(iniPath); err == nil { f, err := ini.Load(iniPath) if err != nil { return nil, fmt.Errorf("加载 app.ini 失败: %w", err) } if s := f.Section("server"); s.HasKey("HTTP_PORT") { if v, err := s.Key("HTTP_PORT").Int(); err == nil { cfg.Port = v } } if s := f.Section("database"); s.HasKey("DSN") { cfg.DBDSN = s.Key("DSN").String() } if s := f.Section("security"); s.HasKey("JWT_SECRET") { cfg.JWTSecret = s.Key("JWT_SECRET").String() } if s := f.Section("paths"); s.HasKey("DATA") { dataRel := s.Key("DATA").String() if !filepath.IsAbs(dataRel) { cfg.DataDir = filepath.Join(workPath, dataRel) } else { cfg.DataDir = dataRel } } if s := f.Section("app"); s.HasKey("DEV_MODE") { cfg.DevMode = s.Key("DEV_MODE").MustBool(true) } } // 环境变量覆盖 if v := os.Getenv("HTTP_PORT"); v != "" { if p, err := strconv.Atoi(v); err == nil { cfg.Port = p } } if v := os.Getenv("DB_DSN"); v != "" { cfg.DBDSN = v } if v := os.Getenv("JWT_SECRET"); v != "" { cfg.JWTSecret = v } if v := os.Getenv("DEV_MODE"); v != "" { cfg.DevMode = strings.EqualFold(v, "true") || v == "1" } // 确保数据目录存在 if err := os.MkdirAll(cfg.DataDir, 0755); err != nil { return nil, fmt.Errorf("创建数据目录失败: %w", err) } // JWT 密钥:留空则自动生成并持久化 if strings.TrimSpace(cfg.JWTSecret) == "" { secretFile := filepath.Join(cfg.DataDir, ".jwt_secret") if data, err := os.ReadFile(secretFile); err == nil && len(data) > 0 { cfg.JWTSecret = string(data) } else { cfg.JWTSecret = generateSecret(32) _ = os.WriteFile(secretFile, []byte(cfg.JWTSecret), 0600) } } return cfg, nil } func generateSecret(n int) string { const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" b := make([]byte, n) for i := range b { b[i] = chars[i%len(chars)] } return string(b) }