🔧 Replaced viper config to environment variables

due 12factor, see: https://12factor.net/config
This commit is contained in:
Maxim Lebedev 2023-01-16 15:24:31 +06:00
parent 30f8be2c6c
commit b67c386ce7
Signed by: toby3d
GPG Key ID: 1F14E25B7C119FC5
2 changed files with 54 additions and 75 deletions

View File

@ -11,68 +11,68 @@ import (
type ( type (
Config struct { Config struct {
Code ConfigCode `yaml:"code"` Code ConfigCode `envPrefix:"CODE_"`
Database ConfigDatabase `yaml:"database"` Database ConfigDatabase `envPrefix:"DATABASE_"`
IndieAuth ConfigIndieAuth `yaml:"indieAuth"` IndieAuth ConfigIndieAuth `envPrefix:"INDIEAUTH_"`
JWT ConfigJWT `yaml:"jwt"` JWT ConfigJWT `envPrefix:"JWT_"`
Server ConfigServer `yaml:"server"` Server ConfigServer `envPrefix:"SERVER_"`
TicketAuth ConfigTicketAuth `yaml:"ticketAuth"` TicketAuth ConfigTicketAuth `envPrefix:"TICKETAUTH_"`
Name string `yaml:"name"` Name string `env:"NAME" envDefault:"IndieAuth"`
RunMode string `yaml:"runMode"` RunMode string `env:"RUN_MODE" envDefault:"dev"`
} }
ConfigServer struct { ConfigServer struct {
CertificateFile string `yaml:"certFile"` CertificateFile string `env:"CERT_FILE"`
Domain string `yaml:"domain"` Domain string `env:"DOMAIN" envDefault:"localhost"`
Host string `yaml:"host"` Host string `env:"HOST" envDefault:"0.0.0.0"`
KeyFile string `yaml:"keyFile"` KeyFile string `env:"KEY_FILE"`
Port string `yaml:"port"` Port string `env:"PORT" envDefault:"3000"`
Protocol string `yaml:"protocol"` Protocol string `env:"PROTOCOL" envDefault:"http"`
RootURL string `yaml:"rootUrl"` RootURL string `env:"ROOT_URL" envDefault:"{{protocol}}://{{domain}}:{{port}}/"`
StaticURLPrefix string `yaml:"staticUrlPrefix"` StaticURLPrefix string `env:"STATIC_URL_PREFIX"`
EnablePprof bool `yaml:"enablePprof"` EnablePprof bool `env:"ENABLE_PPROF"`
} }
ConfigDatabase struct { ConfigDatabase struct {
Path string `yaml:"path"` Path string `env:"PATH"`
Type string `yaml:"type"` // memory Type string `env:"TYPE" envDefault:"memory"` // memory
} }
// Configuration of a one-time code after giving permission to an // Configuration of a one-time code after giving permission to an
// application. The client needs to request the server with this code to // application. The client needs to request the server with this code to
// exchange it for a token or user information. // exchange it for a token or user information.
ConfigCode struct { ConfigCode struct {
Expiry time.Duration `yaml:"expiry"` // 10m Expiry time.Duration `env:"EXPIRY" envDefault:"10m"` // 10m
Length uint8 `yaml:"length"` // 32 Length uint8 `env:"LENGTH" envDefault:"32"` // 32
} }
ConfigJWT struct { ConfigJWT struct {
Expiry time.Duration `yaml:"expiry"` // 1h Expiry time.Duration `env:"EXPIRY" envDefault:"1h"` // 1h
Algorithm string `yaml:"algorithm"` // HS256 Algorithm string `env:"ALGORITHM" envDefault:"HS256"` // HS256
Secret string `yaml:"secret"` Secret string `env:"SECRET"`
NonceLength uint8 `yaml:"nonceLength"` // 22 NonceLength uint8 `env:"NONCE_LENGTH" envDefault:"22"` // 22
} }
ConfigIndieAuth struct { ConfigIndieAuth struct {
Password string `yaml:"password"` Password string `env:"PASSWORD"`
Username string `yaml:"username"` Username string `env:"USERNAME"`
Enabled bool `yaml:"enabled"` // true Enabled bool `env:"ENABLED" envDefault:"true"` // true
} }
ConfigTicketAuth struct { ConfigTicketAuth struct {
Expiry time.Duration `yaml:"expiry"` // 1m Expiry time.Duration `env:"EXPIRY" envDefault:"1m"` // 1m
Length uint8 `yaml:"length"` // 24 Length uint8 `env:"LENGTH" envDefault:"24"` // 24
} }
ConfigRelMeAuth struct { ConfigRelMeAuth struct {
Providers []ConfigRelMeAuthProvider `yaml:"providers"` Providers []ConfigRelMeAuthProvider `envPrefix:"PROVIDERS_"`
Enabled bool `yaml:"enabled"` // true Enabled bool `env:"ENABLED" envDefault:"true"` // true
} }
ConfigRelMeAuthProvider struct { ConfigRelMeAuthProvider struct {
ID string `yaml:"id"` ID string `env:"ID"`
Secret string `yaml:"secret"` Secret string `env:"SECRET"`
Type string `yaml:"type"` Type string `env:"TYPE"`
} }
) )

59
main.go
View File

@ -17,15 +17,14 @@ import (
"net/url" "net/url"
"os" "os"
"os/signal" "os/signal"
"path/filepath"
"runtime" "runtime"
"runtime/pprof" "runtime/pprof"
"strings" "strings"
"syscall" "syscall"
"time" "time"
"github.com/caarlos0/env/v6"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
"github.com/spf13/viper"
"golang.org/x/text/language" "golang.org/x/text/language"
"golang.org/x/text/message" "golang.org/x/text/message"
_ "modernc.org/sqlite" _ "modernc.org/sqlite"
@ -86,9 +85,8 @@ type (
) )
const ( const (
DefaultCacheDuration time.Duration = 8760 * time.Hour // NOTE(toby3d): year DefaultReadTimeout time.Duration = 5 * time.Second
DefaultReadTimeout time.Duration = 5 * time.Second DefaultWriteTimeout time.Duration = 10 * time.Second
DefaultWriteTimeout time.Duration = 10 * time.Second
) )
//nolint:gochecknoglobals //nolint:gochecknoglobals
@ -96,12 +94,16 @@ var (
// NOTE(toby3d): write logs in stdout, see: https://12factor.net/logs // NOTE(toby3d): write logs in stdout, see: https://12factor.net/logs
logger = log.New(os.Stdout, "IndieAuth\t", log.Lmsgprefix|log.LstdFlags|log.LUTC) logger = log.New(os.Stdout, "IndieAuth\t", log.Lmsgprefix|log.LstdFlags|log.LUTC)
config = new(domain.Config) 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 ( var (
configPath, cpuProfilePath, memProfilePath string cpuProfilePath, memProfilePath string
enablePprof bool enablePprof bool
) )
//go:embed assets/* //go:embed assets/*
@ -109,31 +111,15 @@ var staticFS embed.FS
//nolint:gochecknoinits //nolint:gochecknoinits
func init() { func init() {
flag.StringVar(&configPath, "config", filepath.Join(".", "config.yml"), "load specific config")
flag.BoolVar(&enablePprof, "pprof", false, "enable pprof mode") 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() flag.Parse()
viper.AddConfigPath(".") if err := env.Parse(&config, env.Options{
viper.AddConfigPath(filepath.Join(".", "configs")) Prefix: "INDIEAUTH_",
viper.SetConfigName("config") }); err != nil {
viper.SetConfigType("yml") logger.Fatalln(err)
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)
} }
// NOTE(toby3d): The server instance itself can be as a client. // NOTE(toby3d): The server instance itself can be as a client.
@ -147,24 +133,17 @@ func init() {
indieAuthClient.ID = *cid indieAuthClient.ID = *cid
u, err := url.Parse(rootURL) if indieAuthClient.URL[0], err = url.Parse(rootURL); err != nil {
if err != nil {
logger.Fatalln("cannot parse root URL as client URL:", err) logger.Fatalln("cannot parse root URL as client URL:", err)
} }
logo, err := url.Parse(rootURL + config.Server.StaticURLPrefix + "/icon.svg") if indieAuthClient.Logo[0], err = url.Parse(rootURL + config.Server.StaticURLPrefix + "/icon.svg"); err != nil {
if err != nil {
logger.Fatalln("cannot parse root URL as client URL:", err) logger.Fatalln("cannot parse root URL as client URL:", err)
} }
redirectURI, err := url.Parse(rootURL + "callback") if indieAuthClient.RedirectURI[0], err = url.Parse(rootURL + "callback"); err != nil {
if err != nil {
logger.Fatalln("cannot parse root URL as client URL:", err) 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 //nolint:funlen,cyclop // "god object" and the entry point of all modules