123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- package config
- import (
- "fmt"
- "os"
- "strings" // Added for PreviewTLD validation
- "gopkg.in/yaml.v3"
- )
- // Config holds application configuration
- type Config struct {
- Server *Server `yaml:"server"`
- Database *Database `yaml:"database"`
- Auth *Auth `yaml:"auth"`
- Providers map[string]map[string]string `yaml:"providers"`
- Debug bool `yaml:"debug"`
- LocalPreview bool `yaml:"local_preview"` // For development/testing only - use false for production
- PreviewTLD string `yaml:"preview_tld"`
- BuildkitHost string `yaml:"buildkit_host"`
- ReistryUrl string `yaml:"registry_url"` // URL of the Docker registry
- }
- func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
- type plain Config
- err := unmarshal((*plain)(c))
- if err != nil {
- return err
- }
- // Set default preview TLD if not specified
- if c.PreviewTLD == "" {
- c.PreviewTLD = "home.local"
- }
- // Set default Buildkit host if not specified
- if c.BuildkitHost == "" {
- c.BuildkitHost = "unix:///run/buildkit/buildkitd.sock"
- }
- return nil
- }
- // Validate performs a comprehensive validation of the configuration.
- func (c *Config) Validate() error {
- if c.Server == nil {
- return fmt.Errorf("server configuration block is required")
- }
- if err := c.Server.Validate(); err != nil {
- return fmt.Errorf("server config validation failed: %w", err)
- }
- if c.Database == nil {
- return fmt.Errorf("database configuration block is required")
- }
- if err := c.Database.Validate(); err != nil {
- return fmt.Errorf("database config validation failed: %w", err)
- }
- if c.Auth == nil {
- return fmt.Errorf("auth configuration block is required")
- }
- if err := c.Auth.Validate(); err != nil {
- return fmt.Errorf("auth config validation failed: %w", err)
- }
- if len(c.Providers) == 0 { // Corrected: Removed redundant nil check
- return fmt.Errorf("at least one provider configuration is required in 'providers' block")
- }
- for providerName, providerConfig := range c.Providers {
- if len(providerConfig) == 0 {
- return fmt.Errorf("provider '%s' configuration is empty", providerName)
- }
- }
- return nil
- }
- // Server holds server configuration
- type Server struct {
- Host string `yaml:"host"`
- Port int `yaml:"port"`
- Tls *TlsConfig `yaml:"tls"`
- }
- func (s *Server) UnmarshalYAML(unmarshal func(interface{}) error) error {
- type plain Server
- err := unmarshal((*plain)(s))
- if err != nil {
- return err
- }
- if s.Host == "" {
- s.Host = "0.0.0.0" // Default to all interfaces
- }
- if s.Port == 0 {
- s.Port = 443 // Default port
- }
- return nil
- }
- // Validate performs validation for Server configuration.
- func (s *Server) Validate() error {
- if strings.TrimSpace(s.Host) == "" {
- return fmt.Errorf("server host is required and cannot be empty")
- }
- if s.Port <= 0 || s.Port > 65535 {
- return fmt.Errorf("server port must be between 1 and 65535, got %d", s.Port)
- }
- if s.Tls == nil {
- return fmt.Errorf("TLS configuration block is required")
- }
- if err := s.Tls.Validate(); err != nil {
- return fmt.Errorf("TLS config validation failed: %w", err)
- }
- return nil
- }
- // TlsConfig holds TLS configuration
- type TlsConfig struct {
- Enabled bool `yaml:"enabled"`
- CertFile string `yaml:"cert_file"`
- KeyFile string `yaml:"key_file"`
- }
- func (t *TlsConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
- type plain TlsConfig
- err := unmarshal((*plain)(t))
- if err != nil {
- return err
- }
- return nil
- }
- // Validate performs validation for TlsConfig.
- func (t *TlsConfig) Validate() error {
- if t.Enabled {
- if strings.TrimSpace(t.CertFile) == "" {
- return fmt.Errorf("TLS cert_file is required and cannot be empty when TLS is enabled")
- }
- if strings.TrimSpace(t.KeyFile) == "" {
- return fmt.Errorf("TLS key_file is required and cannot be empty when TLS is enabled")
- }
- }
- return nil
- }
- // Database holds database configuration
- type Database struct {
- DSN string `yaml:"dsn"`
- }
- func (d *Database) UnmarshalYAML(unmarshal func(interface{}) error) error {
- type plain Database
- err := unmarshal((*plain)(d))
- if err != nil {
- return err
- }
- return nil
- }
- // Validate performs validation for Database configuration.
- func (d *Database) Validate() error {
- if strings.TrimSpace(d.DSN) == "" {
- return fmt.Errorf("database DSN is required and cannot be empty")
- }
- if !strings.HasPrefix(d.DSN, "file:") && d.DSN != ":memory:" && !strings.Contains(d.DSN, ".db") {
- // This is a basic check, might need refinement based on actual DSN formats used
- fmt.Printf("Warning: DSN '%s' might not be a typical SQLite DSN\n", d.DSN)
- }
- return nil
- }
- // Sql holds SQL database configuration
- type Sqlite struct {
- File string `yaml:"file"`
- }
- func (c *Sqlite) UnmarshalYAML(unmarshal func(interface{}) error) error {
- type plain Sqlite
- err := unmarshal((*plain)(c))
- if err != nil {
- return err
- }
- if c.File == "" {
- return fmt.Errorf("SQLite file is required")
- }
- return nil
- }
- // Auth holds authentication configuration
- type Auth struct {
- PrivateKey string `yaml:"private_key"`
- TokenDuration int `yaml:"token_duration"`
- CleanupInterval int `yaml:"cleanup_interval"`
- }
- func (a *Auth) UnmarshalYAML(unmarshal func(interface{}) error) error {
- type plain Auth
- err := unmarshal((*plain)(a))
- if err != nil {
- return err
- }
- if a.TokenDuration == 0 {
- a.TokenDuration = 3600 // Default to 1 hour
- }
- if a.CleanupInterval == 0 {
- a.CleanupInterval = 3600 // Default to 1 hour
- }
- return nil
- }
- // Validate performs validation for Auth configuration.
- func (a *Auth) Validate() error {
- if strings.TrimSpace(a.PrivateKey) == "" {
- return fmt.Errorf("auth private_key is required and cannot be empty")
- }
- if a.TokenDuration <= 0 {
- return fmt.Errorf("auth token_duration must be a positive integer, got %d", a.TokenDuration)
- }
- if a.CleanupInterval <= 0 {
- return fmt.Errorf("auth cleanup_interval must be a positive integer, got %d", a.CleanupInterval)
- }
- return nil
- }
- func Load(configPath string) (*Config, error) {
- cnf := &Config{}
- b, err := os.ReadFile(configPath)
- if err != nil {
- return nil, err
- }
- err = yaml.Unmarshal(b, cnf)
- if err != nil {
- return nil, err
- }
- // Validate the loaded configuration
- if err := cnf.Validate(); err != nil {
- return nil, fmt.Errorf("configuration validation failed: %w", err)
- }
- return cnf, nil
- }
|