package config import ( "fmt" "os" "path/filepath" "strconv" "strings" "github.com/BurntSushi/toml" ) type Config struct { Server ServerConfig `toml:"server"` Storage StorageConfig `toml:"storage"` Auth AuthConfig `toml:"auth"` Landing LandingConfig `toml:"landing"` } type ServerConfig struct { Host string `toml:"host"` Port int `toml:"port"` BaseURL string `toml:"base_url"` } type StorageConfig struct { DataDir string `toml:"data_dir"` } type AuthConfig struct { RegistrationEnabled bool `toml:"registration_enabled"` } type LandingConfig struct { Enabled bool `toml:"enabled"` DefaultLang string `toml:"default_lang"` } func Default() Config { return Config{ Server: ServerConfig{ Host: "0.0.0.0", Port: 3000, BaseURL: "http://localhost:3000", }, Storage: StorageConfig{ DataDir: "./data", }, Auth: AuthConfig{ RegistrationEnabled: true, }, Landing: LandingConfig{ Enabled: true, DefaultLang: "ru", }, } } func Load(configPath string) (Config, error) { cfg := Default() if envPath := os.Getenv("POLAR_CONFIG"); envPath != "" && configPath == "" { configPath = envPath } if configPath == "" { if _, err := os.Stat("polar.toml"); err == nil { configPath = "polar.toml" } } if configPath != "" { data, err := os.ReadFile(configPath) if err != nil { return cfg, fmt.Errorf("read config file %s: %w", configPath, err) } if err := toml.Unmarshal(data, &cfg); err != nil { return cfg, fmt.Errorf("parse config file %s: %w", configPath, err) } } applyEnv(&cfg) cfg.Server.BaseURL = strings.TrimRight(cfg.Server.BaseURL, "/") if cfg.Server.BaseURL == "" { host := cfg.Server.Host if host == "0.0.0.0" || host == "" { host = "localhost" } cfg.Server.BaseURL = fmt.Sprintf("http://%s:%d", host, cfg.Server.Port) } if cfg.Landing.DefaultLang != "en" && cfg.Landing.DefaultLang != "ru" { cfg.Landing.DefaultLang = "ru" } return cfg, nil } func applyEnv(cfg *Config) { getEnv := os.Getenv if v := getEnv("POLAR_SERVER_HOST"); v != "" { cfg.Server.Host = v } if v := getEnv("POLAR_HOST"); v != "" { cfg.Server.Host = v } if v := getEnv("POLAR_SERVER_PORT"); v != "" { if p, err := strconv.Atoi(v); err == nil && p > 0 { cfg.Server.Port = p } } if v := getEnv("POLAR_PORT"); v != "" { if p, err := strconv.Atoi(v); err == nil && p > 0 { cfg.Server.Port = p } } if v := getEnv("POLAR_SERVER_BASE_URL"); v != "" { cfg.Server.BaseURL = v } if v := getEnv("POLAR_BASE_URL"); v != "" { cfg.Server.BaseURL = v } if v := getEnv("POLAR_STORAGE_DATA_DIR"); v != "" { cfg.Storage.DataDir = v } if v := getEnv("POLAR_DATA_DIR"); v != "" { cfg.Storage.DataDir = v } if v := getEnv("POLAR_AUTH_REGISTRATION_ENABLED"); v != "" { if b, err := strconv.ParseBool(v); err == nil { cfg.Auth.RegistrationEnabled = b } } if v := getEnv("POLAR_LANDING_ENABLED"); v != "" { if b, err := strconv.ParseBool(v); err == nil { cfg.Landing.Enabled = b } } if v := getEnv("POLAR_LANDING_DEFAULT_LANG"); v != "" { cfg.Landing.DefaultLang = strings.ToLower(strings.TrimSpace(v)) } } func (c Config) Addr() string { return fmt.Sprintf("%s:%d", c.Server.Host, c.Server.Port) } func (c Config) DBPath() string { return filepath.Join(c.Storage.DataDir, "polar.db") } func (c Config) ReposDir() string { return filepath.Join(c.Storage.DataDir, "repos") }