From b67c386ce7327ada931d5c3a6018f9f49094ff62 Mon Sep 17 00:00:00 2001 From: Maxim Lebedev Date: Mon, 16 Jan 2023 15:24:31 +0600 Subject: [PATCH] :wrench: Replaced viper config to environment variables due 12factor, see: https://12factor.net/config --- internal/domain/config.go | 70 +++++++++++++++++++-------------------- main.go | 59 +++++++++++---------------------- 2 files changed, 54 insertions(+), 75 deletions(-) diff --git a/internal/domain/config.go b/internal/domain/config.go index 492b1ad..3acfaa7 100644 --- a/internal/domain/config.go +++ b/internal/domain/config.go @@ -11,68 +11,68 @@ import ( type ( Config struct { - Code ConfigCode `yaml:"code"` - Database ConfigDatabase `yaml:"database"` - IndieAuth ConfigIndieAuth `yaml:"indieAuth"` - JWT ConfigJWT `yaml:"jwt"` - Server ConfigServer `yaml:"server"` - TicketAuth ConfigTicketAuth `yaml:"ticketAuth"` - Name string `yaml:"name"` - RunMode string `yaml:"runMode"` + Code ConfigCode `envPrefix:"CODE_"` + Database ConfigDatabase `envPrefix:"DATABASE_"` + IndieAuth ConfigIndieAuth `envPrefix:"INDIEAUTH_"` + JWT ConfigJWT `envPrefix:"JWT_"` + Server ConfigServer `envPrefix:"SERVER_"` + TicketAuth ConfigTicketAuth `envPrefix:"TICKETAUTH_"` + Name string `env:"NAME" envDefault:"IndieAuth"` + RunMode string `env:"RUN_MODE" envDefault:"dev"` } ConfigServer struct { - CertificateFile string `yaml:"certFile"` - Domain string `yaml:"domain"` - Host string `yaml:"host"` - KeyFile string `yaml:"keyFile"` - Port string `yaml:"port"` - Protocol string `yaml:"protocol"` - RootURL string `yaml:"rootUrl"` - StaticURLPrefix string `yaml:"staticUrlPrefix"` - EnablePprof bool `yaml:"enablePprof"` + CertificateFile string `env:"CERT_FILE"` + Domain string `env:"DOMAIN" envDefault:"localhost"` + Host string `env:"HOST" envDefault:"0.0.0.0"` + KeyFile string `env:"KEY_FILE"` + Port string `env:"PORT" envDefault:"3000"` + Protocol string `env:"PROTOCOL" envDefault:"http"` + RootURL string `env:"ROOT_URL" envDefault:"{{protocol}}://{{domain}}:{{port}}/"` + StaticURLPrefix string `env:"STATIC_URL_PREFIX"` + EnablePprof bool `env:"ENABLE_PPROF"` } ConfigDatabase struct { - Path string `yaml:"path"` - Type string `yaml:"type"` // memory + Path string `env:"PATH"` + Type string `env:"TYPE" envDefault:"memory"` // memory } // Configuration of a one-time code after giving permission to an // application. The client needs to request the server with this code to // exchange it for a token or user information. ConfigCode struct { - Expiry time.Duration `yaml:"expiry"` // 10m - Length uint8 `yaml:"length"` // 32 + Expiry time.Duration `env:"EXPIRY" envDefault:"10m"` // 10m + Length uint8 `env:"LENGTH" envDefault:"32"` // 32 } ConfigJWT struct { - Expiry time.Duration `yaml:"expiry"` // 1h - Algorithm string `yaml:"algorithm"` // HS256 - Secret string `yaml:"secret"` - NonceLength uint8 `yaml:"nonceLength"` // 22 + Expiry time.Duration `env:"EXPIRY" envDefault:"1h"` // 1h + Algorithm string `env:"ALGORITHM" envDefault:"HS256"` // HS256 + Secret string `env:"SECRET"` + NonceLength uint8 `env:"NONCE_LENGTH" envDefault:"22"` // 22 } ConfigIndieAuth struct { - Password string `yaml:"password"` - Username string `yaml:"username"` - Enabled bool `yaml:"enabled"` // true + Password string `env:"PASSWORD"` + Username string `env:"USERNAME"` + Enabled bool `env:"ENABLED" envDefault:"true"` // true } ConfigTicketAuth struct { - Expiry time.Duration `yaml:"expiry"` // 1m - Length uint8 `yaml:"length"` // 24 + Expiry time.Duration `env:"EXPIRY" envDefault:"1m"` // 1m + Length uint8 `env:"LENGTH" envDefault:"24"` // 24 } ConfigRelMeAuth struct { - Providers []ConfigRelMeAuthProvider `yaml:"providers"` - Enabled bool `yaml:"enabled"` // true + Providers []ConfigRelMeAuthProvider `envPrefix:"PROVIDERS_"` + Enabled bool `env:"ENABLED" envDefault:"true"` // true } ConfigRelMeAuthProvider struct { - ID string `yaml:"id"` - Secret string `yaml:"secret"` - Type string `yaml:"type"` + ID string `env:"ID"` + Secret string `env:"SECRET"` + Type string `env:"TYPE"` } ) diff --git a/main.go b/main.go index c24fdde..6949814 100644 --- a/main.go +++ b/main.go @@ -17,15 +17,14 @@ import ( "net/url" "os" "os/signal" - "path/filepath" "runtime" "runtime/pprof" "strings" "syscall" "time" + "github.com/caarlos0/env/v6" "github.com/jmoiron/sqlx" - "github.com/spf13/viper" "golang.org/x/text/language" "golang.org/x/text/message" _ "modernc.org/sqlite" @@ -86,9 +85,8 @@ type ( ) const ( - DefaultCacheDuration time.Duration = 8760 * time.Hour // NOTE(toby3d): year - DefaultReadTimeout time.Duration = 5 * time.Second - DefaultWriteTimeout time.Duration = 10 * time.Second + DefaultReadTimeout time.Duration = 5 * time.Second + DefaultWriteTimeout time.Duration = 10 * time.Second ) //nolint:gochecknoglobals @@ -96,12 +94,16 @@ var ( // NOTE(toby3d): write logs in stdout, see: https://12factor.net/logs logger = log.New(os.Stdout, "IndieAuth\t", log.Lmsgprefix|log.LstdFlags|log.LUTC) config = new(domain.Config) - indieAuthClient = new(domain.Client) + indieAuthClient = &domain.Client{ + URL: make([]*url.URL, 1), + Logo: make([]*url.URL, 1), + RedirectURI: make([]*url.URL, 1), + } ) var ( - configPath, cpuProfilePath, memProfilePath string - enablePprof bool + cpuProfilePath, memProfilePath string + enablePprof bool ) //go:embed assets/* @@ -109,31 +111,15 @@ var staticFS embed.FS //nolint:gochecknoinits func init() { - flag.StringVar(&configPath, "config", filepath.Join(".", "config.yml"), "load specific config") flag.BoolVar(&enablePprof, "pprof", false, "enable pprof mode") + flag.StringVar(&cpuProfilePath, "cpuprofile", "", "set path to saving CPU memory profile") + flag.StringVar(&memProfilePath, "memprofile", "", "set path to saving pprof memory profile") flag.Parse() - viper.AddConfigPath(".") - viper.AddConfigPath(filepath.Join(".", "configs")) - viper.SetConfigName("config") - viper.SetConfigType("yml") - - if configPath != "" { - viper.SetConfigFile(configPath) - } - - viper.SetEnvPrefix("INDIEAUTH_") - viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - viper.AutomaticEnv() - viper.WatchConfig() - - var err error - if err = viper.ReadInConfig(); err != nil { - logger.Fatalf("cannot load config from file %s: %v", viper.ConfigFileUsed(), err) - } - - if err = viper.Unmarshal(&config); err != nil { - logger.Fatalln("failed to read config:", err) + if err := env.Parse(&config, env.Options{ + Prefix: "INDIEAUTH_", + }); err != nil { + logger.Fatalln(err) } // NOTE(toby3d): The server instance itself can be as a client. @@ -147,24 +133,17 @@ func init() { indieAuthClient.ID = *cid - u, err := url.Parse(rootURL) - if err != nil { + if indieAuthClient.URL[0], err = url.Parse(rootURL); err != nil { logger.Fatalln("cannot parse root URL as client URL:", err) } - logo, err := url.Parse(rootURL + config.Server.StaticURLPrefix + "/icon.svg") - if err != nil { + if indieAuthClient.Logo[0], err = url.Parse(rootURL + config.Server.StaticURLPrefix + "/icon.svg"); err != nil { logger.Fatalln("cannot parse root URL as client URL:", err) } - redirectURI, err := url.Parse(rootURL + "callback") - if err != nil { + if indieAuthClient.RedirectURI[0], err = url.Parse(rootURL + "callback"); err != nil { logger.Fatalln("cannot parse root URL as client URL:", err) } - - indieAuthClient.URL = []*url.URL{u} - indieAuthClient.Logo = []*url.URL{logo} - indieAuthClient.RedirectURI = []*url.URL{redirectURI} } //nolint:funlen,cyclop // "god object" and the entry point of all modules