// Package config provides application configuration using smartconfig. package config import ( "fmt" "os" "path/filepath" "git.eeqj.de/sneak/smartconfig" "go.uber.org/fx" "sneak.berlin/go/pixa/internal/globals" "sneak.berlin/go/pixa/internal/logger" ) // Default configuration values. const ( DefaultPort = 8080 DefaultStateDir = "./data" ) // Params defines dependencies for Config. type Params struct { fx.In Globals *globals.Globals Logger *logger.Logger } // Config holds application configuration values. type Config struct { Debug bool MaintenanceMode bool MetricsPassword string MetricsUsername string Port int SentryDSN string StateDir string DBURL string } // New creates a new Config instance by loading configuration from file. func New(_ fx.Lifecycle, params Params) (*Config, error) { log := params.Logger.Get() name := params.Globals.Appname var sc *smartconfig.Config var err error // Try loading config from standard locations configPaths := []string{ fmt.Sprintf("/etc/%s/config.yml", name), fmt.Sprintf("/etc/%s/config.yaml", name), filepath.Join(os.Getenv("HOME"), ".config", name, "config.yml"), filepath.Join(os.Getenv("HOME"), ".config", name, "config.yaml"), "config.yml", "config.yaml", } for _, path := range configPaths { if _, statErr := os.Stat(path); statErr == nil { sc, err = smartconfig.NewFromConfigPath(path) if err == nil { log.Info("loaded config file", "path", path) break } log.Warn("failed to parse config file", "path", path, "error", err) } } if sc == nil { log.Info("no config file found, using defaults") } c := &Config{ Debug: getBool(sc, "debug", false), MaintenanceMode: getBool(sc, "maintenance_mode", false), Port: getInt(sc, "port", DefaultPort), StateDir: getString(sc, "state_dir", DefaultStateDir), SentryDSN: getString(sc, "sentry_dsn", ""), MetricsUsername: getString(sc, "metrics.username", ""), MetricsPassword: getString(sc, "metrics.password", ""), } // Build DBURL from StateDir if not explicitly set c.DBURL = getString(sc, "db_url", "") if c.DBURL == "" { c.DBURL = fmt.Sprintf("file:%s/state.sqlite3?_journal_mode=WAL", c.StateDir) } if c.Debug { params.Logger.EnableDebugLogging() } return c, nil } func getString(sc *smartconfig.Config, key, defaultVal string) string { if sc == nil { return defaultVal } val, err := sc.GetString(key) if err != nil { return defaultVal } return val } func getInt(sc *smartconfig.Config, key string, defaultVal int) int { if sc == nil { return defaultVal } val, err := sc.GetInt(key) if err != nil { return defaultVal } return val } func getBool(sc *smartconfig.Config, key string, defaultVal bool) bool { if sc == nil { return defaultVal } val, err := sc.GetBool(key) if err != nil { return defaultVal } return val }