// pkg/config/config.go package config import ( "fmt" "os" "git.linuxforward.com/byom/byom-golang-lib/pkg/auth" "git.linuxforward.com/byom/byom-golang-lib/pkg/database" "git.linuxforward.com/byom/byom-golang-lib/pkg/logger" "git.linuxforward.com/byom/byom-golang-lib/pkg/server" "git.linuxforward.com/byom/byom-golang-lib/pkg/smtp" "git.linuxforward.com/byom/byom-golang-lib/pkg/storage" "git.linuxforward.com/byom/byom-golang-lib/pkg/webhook" "gopkg.in/yaml.v3" ) type Config struct { App *App `yaml:"app"` Auth *auth.Config `yaml:"auth"` Server *server.Config `yaml:"server"` Database *database.Config `yaml:"database"` Log *logger.Config `yaml:"log"` Storage *storage.Config `yaml:"storage"` Smtp *smtp.Config `yaml:"smtp,omitempty"` Webhook *webhook.Config `yaml:"webhook,omitempty"` 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 NewFieldError("validate", "root", "app", "", ErrMissingRequired, "app section is required") } // Conditional validation based on app name switch c.App.Name { case "design": if c.CloudComputing == nil { return NewFieldError("validate", "root", "cloud_computing", "", ErrMissingRequired, "cloud_computing section is required for design app") } case "trends": if c.SocialNetworks == nil { return NewFieldError("validate", "root", "social_networks", "", ErrMissingRequired, "social_networks section is required for trends app") } } 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 NewFieldError("validate", "app", "name", "", ErrMissingRequired, "app name is required") } if c.Environment == "" { c.Environment = "development" } // Validate app name validNames := map[string]bool{"core": true, "design": true, "trends": true} if !validNames[c.Name] { return NewFieldError("validate", "app", "name", c.Name, ErrInvalidAppName, "app name must be one of: core, design, trends") } 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 NewFieldError("validate", "cloud_computing", "provider", "", ErrMissingRequired, "provider is required") } if c.Region == "" { return NewFieldError("validate", "cloud_computing", "region", "", ErrMissingRequired, "region is required") } if c.MaxCapacity <= 0 { return NewFieldError("validate", "cloud_computing", "max_capacity", fmt.Sprintf("%d", c.MaxCapacity), ErrInvalidValue, "max_capacity must be greater than 0") } 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 NewFieldError("validate", "social_networks", "networks", "", ErrMissingRequired, "at least one social network is required") } for i, network := range c.Networks { section := fmt.Sprintf("social_networks.networks[%d]", i) if network.Name == "" { return NewFieldError("validate", section, "name", "", ErrMissingRequired, "network name is required") } if network.APIKey == "" { return NewFieldError("validate", section, "api_key", "", ErrMissingRequired, "API key is required") } if network.APIToken == "" { return NewFieldError("validate", section, "api_token", "", ErrMissingRequired, "API token is required") } } return nil } func ReadConfig(configPath string) (*Config, error) { data, err := os.ReadFile(configPath) if err != nil { if os.IsNotExist(err) { return nil, NewError("read", ErrFileNotFound, configPath) } if os.IsPermission(err) { return nil, NewError("read", ErrFilePermission, configPath) } return nil, NewError("read", err, "failed to read config file") } config := &Config{} err = yaml.Unmarshal(data, config) if err != nil { return nil, NewError("parse", ErrInvalidYAML, err.Error()) } // Validate all config sections that implement Validator interface validators := []Validator{} if config.Storage != nil { validators = append(validators, config.Storage) } if config.Database != nil { validators = append(validators, config.Database) } if config.Log != nil { validators = append(validators, config.Log) } if config.Server != nil { validators = append(validators, config.Server) } if config.Auth != nil { validators = append(validators, config.Auth) } if config.Webhook != nil { validators = append(validators, config.Webhook) } if err := ValidateAll(validators...); err != nil { return nil, err } return config, nil }