2025-03-21 11:31:30 +03:00

60 lines
2.4 KiB
Go

package config
import (
"fmt"
"github.com/go-playground/validator/v10"
"github.com/ilyakaznacheev/cleanenv"
"net/url"
"reflect"
"slices"
)
type Config struct {
// Format: host:port
WebserverAddress string `env:"WEBSERVER_ADDRESS" env-default:"0.0.0.0:5000" validate:"hostname_port"`
NatsUrl string `env:"NATS_URL" env-default:"nats://localhost:4222" validate:"url"`
RedisUrl string `env:"REDIS_URL" env-default:"localhost:6379" validate:"url"`
Debug bool `env:"DEBUG"`
// Format: scheme://user:pass@host:port (supported schemes: http, https, socks)
Proxy string `env:"PROXY" env-default:"" validate:"omitempty,proxy"`
// TaskRateLimitEvery and TaskRateLimitBurst are parameters for Token Bucket algorithm
// for task rate limiter (don't apply to cache).
// A token is added to the bucket every TaskRateLimitEvery seconds.
TaskRateLimitEvery float64 `env:"TASK_RATE_LIMIT_EVERY" env-default:"60" validate:"number,gt=0"`
TaskRateLimitBurst int `env:"TASK_RATE_LIMIT_BURST" env-default:"10" validate:"number,gte=0"`
// PerDomainRateLimitEvery and PerDomainRateLimitCapacity are params for LeakyBucket alrogithm
// for per-domain rate limiting of outgoing queries.
// Request to domain limited to 1 per PerDomainRateLimitEvery seconds.
PerDomainRateLimitEvery float64 `env:"PER_DOMAIN_RATE_LIMIT_EVERY" env-default:"2" validate:"number,gt=0"`
PerDomainRateLimitCapacity int `env:"PER_DOMAIN_RATE_LIMIT_CAPACITY" env-default:"10" validate:"number,gt=0"`
// IP ranges of reverse proxies for correct real ip detection (cidr format, sep. by comma)
TrustedIpRanges []string `env:"TRUSTED_IP_RANGES" env-default:"" validate:"omitempty,dive,cidr"`
RealIpHeader string `env:"REAL_IP_HEADER" env-default:"" validate:"omitempty"`
}
func Read() (Config, error) {
var cfg Config
err := cleanenv.ReadEnv(&cfg)
if err != nil {
return Config{}, err
}
validate := validator.New()
if err := validate.RegisterValidation("proxy", validateProxy); err != nil {
panic(fmt.Errorf("register validation: %w", err))
}
err = validate.Struct(cfg)
if err == nil {
fmt.Printf("Config: %+v\n", cfg)
}
return cfg, err
}
func validateProxy(fl validator.FieldLevel) bool {
if fl.Field().Kind() != reflect.String {
return false
}
validSchemes := []string{"http", "https", "socks"}
pUrl, err := url.Parse(fl.Field().String())
return err == nil && slices.Contains(validSchemes, pUrl.Scheme) && pUrl.Opaque == "" && pUrl.Path == ""
}