package config import ( "fmt" "os" "regexp" "gopkg.in/yaml.v3" ) type Config struct { Database DatabaseConfig `yaml:"database"` AI AIConfig `yaml:"ai"` Security SecurityConfig `yaml:"security"` GRPC GRPCConfig `yaml:"grpc"` Logging LoggingConfig `yaml:"logging"` } type DatabaseConfig struct { Host string `yaml:"host"` Port int `yaml:"port"` Name string `yaml:"name"` User string `yaml:"user"` Password string `yaml:"password"` SSLMode string `yaml:"ssl_mode"` MaxConns int `yaml:"max_conns"` MinConns int `yaml:"min_conns"` } type AIConfig struct { OpenAIKey string `yaml:"openai_key"` PerplexityKey string `yaml:"perplexity_key"` } type SecurityConfig struct { JWTSecret string `yaml:"jwt_secret"` CryptoSecret string `yaml:"crypto_secret"` } type GRPCConfig struct { Port int `yaml:"port"` MaxConnections int `yaml:"max_connections"` } type LoggingConfig struct { Level string `yaml:"level"` } func Load(path string) (*Config, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("failed to read config file: %w", err) } data = []byte(expandEnvWithDefaults(string(data))) var cfg Config if err := yaml.Unmarshal(data, &cfg); err != nil { return nil, fmt.Errorf("failed to unmarshal config: %w", err) } if err := cfg.validate(); err != nil { return nil, fmt.Errorf("invalid config: %w", err) } return &cfg, nil } func expandEnvWithDefaults(s string) string { re := regexp.MustCompile(`\$\{([^:}]+):([^}]*)\}`) result := re.ReplaceAllStringFunc(s, func(match string) string { parts := re.FindStringSubmatch(match) if len(parts) != 3 { return match } envVar := parts[1] defaultVal := parts[2] if val := os.Getenv(envVar); val != "" { return val } return defaultVal }) return os.ExpandEnv(result) } func (c *Config) validate() error { if c.Database.Host == "" { return fmt.Errorf("database host is required") } if c.Database.Name == "" { return fmt.Errorf("database name is required") } if c.Database.User == "" { return fmt.Errorf("database user is required") } if c.Database.Password == "" { return fmt.Errorf("database password is required") } if c.Security.JWTSecret == "" { return fmt.Errorf("JWT secret is required") } if c.Security.CryptoSecret == "" { return fmt.Errorf("crypto secret is required") } return nil } func (c *Config) DatabaseURL() string { return fmt.Sprintf("postgres://%s:%s@%s:%d/%s?sslmode=%s", c.Database.User, c.Database.Password, c.Database.Host, c.Database.Port, c.Database.Name, c.Database.SSLMode) }