package server import ( "fmt" "os" "gopkg.in/yaml.v3" ) /* Server configuration loaded from YAML. * Supports TLS, Basic Auth via htpasswd, and per-repo access control. */ // Config is the server configuration type Config struct { Server ServerConfig `yaml:"server"` Auth AuthConfig `yaml:"auth"` Storage StorageConfig `yaml:"storage"` Logging LoggingConfig `yaml:"logging"` Repos []RepoConfig `yaml:"repositories"` } // ServerConfig contains HTTP server settings type ServerConfig struct { Host string `yaml:"host"` Port int `yaml:"port"` BaseURL string `yaml:"base_url"` TLS TLSConfig `yaml:"tls"` } // TLSConfig contains TLS settings type TLSConfig struct { Enabled bool `yaml:"enabled"` Cert string `yaml:"cert"` Key string `yaml:"key"` } // AuthConfig contains authentication settings type AuthConfig struct { Enabled bool `yaml:"enabled"` HtpasswdFile string `yaml:"htpasswd_file"` Realm string `yaml:"realm"` } // StorageConfig contains storage settings type StorageConfig struct { Root string `yaml:"root"` MaxRepoSize string `yaml:"max_repo_size"` MaxFileSize string `yaml:"max_file_size"` } // LoggingConfig contains logging settings type LoggingConfig struct { Level string `yaml:"level"` // debug, info, warn, error Format string `yaml:"format"` // json, text File string `yaml:"file"` // log file path (empty for stdout) } // RepoConfig is per-repository configuration type RepoConfig struct { Name string `yaml:"name"` Path string `yaml:"path"` // relative to storage.root Public bool `yaml:"public"` // allow anonymous read AllowedUsers []string `yaml:"allowed_users"` // full access ReadOnlyUsers []string `yaml:"read_only_users"` // read-only access } // LoadConfig loads configuration from YAML file func LoadConfig(path string) (*Config, error) { data, err := os.ReadFile(path) if err != nil { return nil, fmt.Errorf("read config: %w", err) } cfg := &Config{} if err := yaml.Unmarshal(data, cfg); err != nil { return nil, fmt.Errorf("parse config: %w", err) } /* apply defaults */ if cfg.Server.Host == "" { cfg.Server.Host = "0.0.0.0" } if cfg.Server.Port == 0 { cfg.Server.Port = 8080 } if cfg.Auth.Realm == "" { cfg.Auth.Realm = "Larc Repository" } if cfg.Logging.Level == "" { cfg.Logging.Level = "info" } if cfg.Logging.Format == "" { cfg.Logging.Format = "json" } return cfg, nil } // DefaultConfig returns default configuration func DefaultConfig() *Config { return &Config{ Server: ServerConfig{ Host: "0.0.0.0", Port: 8080, }, Auth: AuthConfig{ Enabled: false, Realm: "Larc Repository", }, Storage: StorageConfig{ Root: "./repos", MaxRepoSize: "10GB", MaxFileSize: "100MB", }, Logging: LoggingConfig{ Level: "info", Format: "json", }, } } // Validate validates the configuration func (c *Config) Validate() error { if c.Auth.Enabled && c.Auth.HtpasswdFile == "" { return fmt.Errorf("auth enabled but htpasswd_file not set") } if c.Server.TLS.Enabled { if c.Server.TLS.Cert == "" || c.Server.TLS.Key == "" { return fmt.Errorf("TLS enabled but cert or key not set") } } return nil } // GetRepoConfig returns config for a specific repository func (c *Config) GetRepoConfig(name string) *RepoConfig { for i := range c.Repos { if c.Repos[i].Name == name { return &c.Repos[i] } } return nil } // IsUserAllowed checks if user has access to repository func (r *RepoConfig) IsUserAllowed(username string, writeAccess bool) bool { /* public repos allow anonymous read */ if r.Public && !writeAccess && username == "" { return true } /* check allowed users (full access) */ for _, u := range r.AllowedUsers { if u == username { return true } } /* check read-only users */ if !writeAccess { for _, u := range r.ReadOnlyUsers { if u == username { return true } } } return false }