package config import ( "fmt" "os" "strconv" "github.com/joho/godotenv" "gopkg.in/yaml.v3" ) type Config struct { Token TokenConfig `yaml:"token" json:"token"` Server ServerConfig `yaml:"server" json:"server"` DB DatabaseConfig `yaml:"db" json:"db"` } type ServerConfig struct { Port string `yaml:"port" json:"port"` } type DatabaseConfig struct { Username string `yaml:"username" json:"username"` Password string // Из переменной окружения, не из YAML Host string `yaml:"host" json:"host"` Port string `yaml:"port" json:"port"` Sslmode string `yaml:"sslmode" json:"sslmode"` DBname string `yaml:"dbname" json:"dbname"` } func LoadConfig(absolutePath string) (*Config, error) { // Загружаем .env файл (игнорируем ошибку, если файла нет) _ = godotenv.Load() config := &Config{} // Читаем базовую конфигурацию из YAML (без секретов) file, err := os.Open(absolutePath) if err != nil { return nil, err } defer file.Close() decoder := yaml.NewDecoder(file) if err := decoder.Decode(config); err != nil { return nil, err } // Переопределяем секретные данные из переменных окружения if err := loadFromEnv(config); err != nil { return nil, err } return config, nil } // loadFromEnv загружает секретные данные из переменных окружения func loadFromEnv(config *Config) error { // Пароль БД (обязательный) dbPassword := os.Getenv("DB_PASSWORD") if dbPassword == "" { return fmt.Errorf("DB_PASSWORD environment variable is required") } config.DB.Password = dbPassword // Access Token Secret (обязательный) accessTokenSecret := os.Getenv("ACCESS_TOKEN_SECRET") if accessTokenSecret == "" { return fmt.Errorf("ACCESS_TOKEN_SECRET environment variable is required") } config.Token.AccessToken.SecretWord = accessTokenSecret // Refresh Token Secret (обязательный) refreshTokenSecret := os.Getenv("REFRESH_TOKEN_SECRET") if refreshTokenSecret == "" { return fmt.Errorf("REFRESH_TOKEN_SECRET environment variable is required") } config.Token.RefreshToken.SecretWord = refreshTokenSecret // Опциональные переопределения (если заданы в env) if accessTTL := os.Getenv("ACCESS_TOKEN_TTL_MINUTES"); accessTTL != "" { ttl, err := strconv.Atoi(accessTTL) if err != nil { return fmt.Errorf("invalid ACCESS_TOKEN_TTL_MINUTES: %w", err) } config.Token.AccessToken.TTLInMinutes = ttl } if refreshTTL := os.Getenv("REFRESH_TOKEN_TTL_MINUTES"); refreshTTL != "" { ttl, err := strconv.Atoi(refreshTTL) if err != nil { return fmt.Errorf("invalid REFRESH_TOKEN_TTL_MINUTES: %w", err) } config.Token.RefreshToken.TTLInMinutes = ttl } if dbHost := os.Getenv("DB_HOST"); dbHost != "" { config.DB.Host = dbHost } if dbPort := os.Getenv("DB_PORT"); dbPort != "" { config.DB.Port = dbPort } if dbName := os.Getenv("DB_NAME"); dbName != "" { config.DB.DBname = dbName } if dbUser := os.Getenv("DB_USER"); dbUser != "" { config.DB.Username = dbUser } if serverPort := os.Getenv("SERVER_PORT"); serverPort != "" { config.Server.Port = serverPort } return nil }