// pkg/config/config.go package config import ( "fmt" "os" "git.linuxforward.com/byom/byom-golang-lib/pkg/errors" "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" ) type Config struct { App *App `yaml:"app"` Server *Server `yaml:"server"` Database *Database `yaml:"database"` Log *Log `yaml:"log"` Storage *Storage `yaml:"storage"` CloudComputing *CloudComputing `yaml:"cloud_computing,omitempty"` SocialNetworks *SocialNetworks `yaml:"social_networks,omitempty"` } func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { type plain Config err := unmarshal((*plain)(c)) if err != nil { return err } if c.App == nil { return errors.NewConfigError("app", errors.ErrInvalidInput) } // Conditional validation based on app name switch c.App.Name { case "design": if c.CloudComputing == nil { return errors.NewConfigError("cloud_computing", errors.ErrInvalidInput) } case "trends": if c.SocialNetworks == nil { return errors.NewConfigError("social_networks", errors.ErrInvalidInput) } } return nil } type App struct { Name string `yaml:"name"` Environment string `yaml:"environment"` } func (c *App) UnmarshalYAML(unmarshal func(interface{}) error) error { type plain App err := unmarshal((*plain)(c)) if err != nil { return err } if c.Name == "" { return errors.NewConfigError("app.name", errors.ErrInvalidInput) } if c.Environment == "" { c.Environment = "development" } // Validate app name validNames := map[string]bool{"core": true, "design": true, "trends": true} if !validNames[c.Name] { return errors.NewConfigError("app.name", errors.ErrInvalidInput) } return nil } type Server struct { Port int `yaml:"port"` RequestTimeout int `yaml:"request_timeout"` // in seconds AllowedOrigins []string `yaml:"allowed_origins"` } func (c *Server) UnmarshalYAML(unmarshal func(interface{}) error) error { type plain Server err := unmarshal((*plain)(c)) if err != nil { return err } if c.Port == 0 { c.Port = 8080 } if c.RequestTimeout == 0 { c.RequestTimeout = 30 } return nil } type Database struct { Path string `yaml:"path"` MaxOpenConns int `yaml:"max_open_conns"` MaxIdleConns int `yaml:"max_idle_conns"` ConnMaxLifetime int `yaml:"conn_max_lifetime"` Pragmas []string `yaml:"pragmas"` } func (c *Database) UnmarshalYAML(unmarshal func(interface{}) error) error { type plain Database err := unmarshal((*plain)(c)) if err != nil { return err } if c.Path == "" { return errors.NewConfigError("database.path", errors.ErrInvalidInput) } if c.MaxOpenConns == 0 { c.MaxOpenConns = 1 // SQLite recommendation } if c.MaxIdleConns == 0 { c.MaxIdleConns = 1 } if c.ConnMaxLifetime == 0 { c.ConnMaxLifetime = 300 // 5 minutes } return nil } type Log struct { Level string `yaml:"level"` Format string `yaml:"format"` // "json" or "text" NoColor bool `yaml:"no_color"` } func (c *Log) UnmarshalYAML(unmarshal func(interface{}) error) error { type plain Log err := unmarshal((*plain)(c)) if err != nil { return err } if c.Level == "" { c.Level = "info" } if c.Format == "" { c.Format = "text" } // Validate log level _, err = logrus.ParseLevel(c.Level) if err != nil { return errors.NewConfigError("log.level", err) } return nil } type Storage struct { Endpoint string `yaml:"endpoint"` AccessKeyID string `yaml:"access_key_id"` SecretAccessKey string `yaml:"secret_access_key"` UseSSL bool `yaml:"use_ssl"` BucketName string `yaml:"bucket_name"` } func (c *Storage) UnmarshalYAML(unmarshal func(interface{}) error) error { type plain Storage err := unmarshal((*plain)(c)) if err != nil { return err } if c.Endpoint == "" { return errors.NewConfigError("storage.endpoint", errors.ErrInvalidInput) } if c.AccessKeyID == "" { return errors.NewConfigError("storage.access_key_id", errors.ErrInvalidInput) } if c.SecretAccessKey == "" { return errors.NewConfigError("storage.secret_access_key", errors.ErrInvalidInput) } if c.BucketName == "" { return errors.NewConfigError("storage.bucket_name", errors.ErrInvalidInput) } return nil } type CloudComputing struct { Provider string `yaml:"provider"` Region string `yaml:"region"` MaxCapacity int `yaml:"max_capacity"` } func (c *CloudComputing) UnmarshalYAML(unmarshal func(interface{}) error) error { type plain CloudComputing err := unmarshal((*plain)(c)) if err != nil { return err } if c.Provider == "" { return errors.NewConfigError("cloud_computing.provider", errors.ErrInvalidInput) } if c.Region == "" { return errors.NewConfigError("cloud_computing.region", errors.ErrInvalidInput) } if c.MaxCapacity <= 0 { return errors.NewConfigError("cloud_computing.max_capacity", errors.ErrInvalidInput) } return nil } type SocialNetworks struct { Networks []SocialNetwork `yaml:"networks"` } type SocialNetwork struct { Name string `yaml:"name"` APIKey string `yaml:"api_key"` APIToken string `yaml:"api_token"` } func (c *SocialNetworks) UnmarshalYAML(unmarshal func(interface{}) error) error { type plain SocialNetworks err := unmarshal((*plain)(c)) if err != nil { return err } if len(c.Networks) == 0 { return errors.NewConfigError("social_networks", errors.ErrInvalidInput) } for i, network := range c.Networks { section := fmt.Sprintf("social_networks.networks[%d]", i) if network.Name == "" { return errors.NewConfigError(section+".name", errors.ErrInvalidInput) } if network.APIKey == "" { return errors.NewConfigError(section+".api_key", errors.ErrInvalidInput) } if network.APIToken == "" { return errors.NewConfigError(section+".api_token", errors.ErrInvalidInput) } } return nil } func ReadConfig(configPath string) (*Config, error) { data, err := os.ReadFile(configPath) if err != nil { return nil, errors.NewConfigError("read", err) } config := &Config{} err = yaml.Unmarshal(data, config) if err != nil { return nil, errors.NewConfigError("parse", err) } return config, nil }