go: add settings struct
All checks were successful
continuous-integration/drone/push Build is passing

* let the settings struct be the single source of truth
* rm app fields that are covered by settings
* pass around a pointer to settings instead of config
* consolidate config+flags into settings on start-up
* update tests
* rm empty settings.go file

fixes #4
This commit is contained in:
leo 2023-05-03 02:18:29 +02:00
parent 9ab2d0ae0b
commit c4d0cb209b
Signed by: wanderer
SSH Key Fingerprint: SHA256:Dp8+iwKHSlrMEHzE3bJnPng70I7LEsa3IJXRH/U+idQ
12 changed files with 273 additions and 124 deletions

View File

@ -4,7 +4,7 @@ import (
"embed"
"errors"
"git.dotya.ml/mirre-mt/pcmt/config"
"git.dotya.ml/mirre-mt/pcmt/app/settings"
"git.dotya.ml/mirre-mt/pcmt/ent"
"git.dotya.ml/mirre-mt/pcmt/slogging"
"github.com/labstack/echo/v4"
@ -15,11 +15,9 @@ type App struct {
logger *slogging.Logger
initialised bool
embeds Embeds
config *config.Config
version string
templatesPath string
assetsPath string
develMode bool
setting *settings.Settings
jwtkey string //nolint:unused
encryptionKey string //nolint:unused
dbConnect string //nolint:unused
@ -32,28 +30,36 @@ type Embeds struct {
}
// Init allows setting App's important fields - once.
func (a *App) Init(version string, logger *slogging.Logger, conf *config.Config, dbclient *ent.Client) error {
func (a *App) Init(s *settings.Settings, logger *slogging.Logger, dbclient *ent.Client) error {
if !a.initialised {
e := echo.New()
a.e = e
a.logger = logger
a.Logger().Infof("app version: %s", version)
a.version = version
a.setting = s
a.config = conf
a.Logger().Infof("app config set to %#v", a.config)
if a.setting.IsDevel() {
a.setDevel()
}
a.Logger().Infof("app version: %s", a.setting.Version())
a.Logger().Info("saving db connection string")
a.db = dbclient
if a.templatesPath == "" {
a.templatesPath = "templates"
a.setting.SetTemplatesPath("templates")
} else {
a.setting.SetTemplatesPath(a.templatesPath)
}
if a.assetsPath == "" {
a.assetsPath = "assets"
a.setting.SetAssetsPath("assets")
} else {
a.setting.SetAssetsPath(a.assetsPath)
}
a.initialised = true
@ -75,6 +81,27 @@ func (a *App) Logger() *slogging.Logger {
return a.logger
}
// PrintConfiguration outputs relevant settings of the application to console.
func (a *App) PrintConfiguration() {
if a.setting != nil {
if a.setting.IsLive() {
a.Logger().Info("app config: live mode enabled")
}
if a.setting.IsDevel() {
a.Logger().Info("app config: devel mode enabled - make sure that browser-sync is running")
}
return
}
panic(errors.New("somehow we got here with config unset - contact the developer"))
}
func (a *App) SetSettings(s *settings.Settings) {
a.setting = s
}
// SetEmbeds saves the embedded files to application state.
func (a *App) SetEmbeds(templates, assets embed.FS) {
a.logger.Info("setting embeds")
@ -82,18 +109,9 @@ func (a *App) SetEmbeds(templates, assets embed.FS) {
a.embeds.assets = assets
}
// SetDevel puts the app in devel mode, which loads a browser-sync script in
// setDevel puts the app in devel mode, which loads a browser-sync script in
// templates and expects browser-sync running.
func (a *App) SetDevel() {
if !a.Config().DevelMode {
a.Logger().Info("overriding configuration value of DevelMode based on a flag")
a.Config().DevelMode = true
} else {
a.Logger().Info("DevelMode already set in the config")
}
a.develMode = true
func (a *App) setDevel() {
a.Logger().Info("enabling debug logging for Echo")
a.e.Debug = true

View File

@ -8,7 +8,7 @@ import (
)
func (a *App) getAssets() http.FileSystem {
live := a.config.LiveMode
live := a.setting.IsLive()
if live {
a.logger.Info("assets loaded in live mode")
@ -38,7 +38,7 @@ func (a *App) getAssets() http.FileSystem {
}
func (a *App) getTemplates() fs.FS {
live := a.config.LiveMode
live := a.setting.IsLive()
if live {
a.logger.Info("templates loaded in live mode")

View File

@ -1,39 +1 @@
package app
import (
"errors"
"git.dotya.ml/mirre-mt/pcmt/config"
)
// // SetConfig takes one argument - the config - and sets it if the config has
// // not been set.
// func (a *App) SetConfig(c *config.Config) {
// if a.config == nil {
// a.config = c
// } else {
// a.logger.Println("config already set, not setting it again")
// }
// }
// Config returns config.
func (a *App) Config() *config.Config {
return a.config
}
// PrintConfiguration outputs relevant settings of the application to console.
func (a *App) PrintConfiguration() {
if a.Config() != nil {
if a.Config().LiveMode {
a.Logger().Info("app config: live mode enabled")
}
if a.Config().DevelMode {
a.Logger().Info("app config: devel mode enabled - make sure that browser-sync is running")
}
return
}
panic(errors.New("somehow we got here with config unset - contact the developer"))
}

View File

@ -10,12 +10,12 @@ import (
func (a *App) SetupRoutes() {
e := a.E()
conf := a.Config()
setting := a.setting
assets := http.FileServer(a.getAssets())
tmpls := a.getTemplates()
// run this before declaring any handler funcs.
handlers.InitHandlers(a.version, a.templatesPath, conf, tmpls)
handlers.InitHandlers(setting, tmpls)
// keep /static/* as a compatibility fallback for /assets.
e.GET(

View File

@ -8,6 +8,7 @@ import (
_ "github.com/xiaoqidun/entps"
"git.dotya.ml/mirre-mt/pcmt/app/settings"
"git.dotya.ml/mirre-mt/pcmt/config"
"git.dotya.ml/mirre-mt/pcmt/ent"
"git.dotya.ml/mirre-mt/pcmt/slogging"
@ -22,7 +23,14 @@ var conf = &config.Config{
SessionCookieSecret: "secret",
}
var (
host = new(string)
port = new(int)
devel = new(bool)
)
func TestStaticRoute(t *testing.T) {
setting := settings.New()
a := &App{
templatesPath: "../templates",
assetsPath: "../assets",
@ -33,7 +41,13 @@ func TestStaticRoute(t *testing.T) {
defer db.Close()
err := a.Init("test", log, conf, db)
*host = ""
*port = 3500
*devel = true
setting.Consolidate(conf, host, port, devel, "test")
err := a.Init(setting, log, db)
if err != nil {
t.Errorf("failed to initialise app: %v", a)
}
@ -65,6 +79,7 @@ func TestStaticRoute(t *testing.T) {
}
func BenchmarkStatic(b *testing.B) {
setting := settings.New()
a := &App{
templatesPath: "../templates",
assetsPath: "../assets",
@ -74,7 +89,9 @@ func BenchmarkStatic(b *testing.B) {
defer db.Close()
err := a.Init("test", log, conf, db)
setting.Consolidate(conf, host, port, devel, "bench")
err := a.Init(setting, log, db)
if err != nil {
b.Errorf("failed to initialise app: %v", a)
}
@ -90,6 +107,7 @@ func BenchmarkStatic(b *testing.B) {
}
func BenchmarkStatic2(b *testing.B) {
setting := settings.New()
a := &App{
templatesPath: "../templates",
assetsPath: "../assets",
@ -99,7 +117,9 @@ func BenchmarkStatic2(b *testing.B) {
defer db.Close()
err := a.Init("test", log, conf, db)
setting.Consolidate(conf, host, port, devel, "bench")
err := a.Init(setting, log, db)
if err != nil {
b.Errorf("failed to initialise app: %v", a)
}

163
app/settings/settings.go Normal file
View File

@ -0,0 +1,163 @@
package settings
import (
"git.dotya.ml/mirre-mt/pcmt/config"
"git.dotya.ml/mirre-mt/pcmt/slogging"
)
type Settings struct {
host string
port int
appName string
isLive bool
isDevel bool
sessionCookieName string
sessionCookieSecret string
assetsPath string
templatesPath string
version string
}
// New returns a new instance of the settings struct.
func New() *Settings {
return &Settings{}
}
// Consolidate reconciles whatever values are set in config and via flags and
// sets it to one place that should be regarded as a single source of truth -
// the settings struct. Order of preference for values is (from higher to
// lower) as follows: flag -> Env var -> configuration file.
func (s *Settings) Consolidate(conf *config.Config, host *string, port *int, devel *bool, version string) {
log := slogging.GetLogger()
log.Debug("starting to consolidate settings")
log.Debug("parsing config")
s.SetHost(conf.Host)
s.SetPort(conf.Port)
s.SetAppName(conf.AppName)
s.SetIsLive(conf.LiveMode)
s.SetIsDevel(conf.DevelMode)
s.SetSessionCookieName(conf.SessionCookieName)
s.SetSessionCookieSecret(conf.SessionCookieSecret)
log.Debug("checking flag overrides")
overrideMsg := "overriding '%s' based on a flag: %+v"
if h := *host; h != "unset" && h != conf.Host {
log.Debugf(overrideMsg, "host", h)
s.SetHost(h)
}
if p := *port; p != 0 && p > 0 && p < 65536 && p != conf.Port {
log.Debugf(overrideMsg, "port", p)
s.SetPort(p)
}
if d := *devel; d != conf.DevelMode {
log.Debugf(overrideMsg, "develMode", devel)
}
s.SetVersion(version)
}
// Host returns the host.
func (s *Settings) Host() string {
return s.host
}
// Port returns the port.
func (s *Settings) Port() int {
return s.port
}
// AppName returns the appName.
func (s *Settings) AppName() string {
return s.appName
}
// IsLive returns the value of isLive of the receiver.
func (s *Settings) IsLive() bool {
return s.isLive
}
// IsDevel returns the value of isDevel of the receiver.
func (s *Settings) IsDevel() bool {
return s.isDevel
}
// SessionCookieName returns the sessionCookieName.
func (s *Settings) SessionCookieName() string {
return s.sessionCookieName
}
// SessionCookieSecret returns the sessionCookieSecret.
func (s *Settings) SessionCookieSecret() string {
return s.sessionCookieSecret
}
// AssetsPath returns the assetsPath.
func (s *Settings) AssetsPath() string {
return s.assetsPath
}
// TemplatesPath returns the templatesPath.
func (s *Settings) TemplatesPath() string {
return s.templatesPath
}
// Version returns the version.
func (s *Settings) Version() string {
return s.version
}
// SetHost sets the host.
func (s *Settings) SetHost(host string) {
s.host = host
}
// SetPort sets the port.
func (s *Settings) SetPort(port int) {
s.port = port
}
// SetAppName sets the appName.
func (s *Settings) SetAppName(appName string) {
s.appName = appName
}
// SetIsLive sets the value of isLive of the receiver.
func (s *Settings) SetIsLive(live bool) {
s.isLive = live
}
// SetIsDevel sets the value of isDevel of the receiver.
func (s *Settings) SetIsDevel(devel bool) {
s.isDevel = devel
}
// SetSessionCookieName sets the sessionCookieName.
func (s *Settings) SetSessionCookieName(sessionCookieName string) {
s.sessionCookieName = sessionCookieName
}
// SetSessionCookieSecret sets the sessionCookieSecret.
func (s *Settings) SetSessionCookieSecret(sessionCookieSecret string) {
s.sessionCookieSecret = sessionCookieSecret
}
// SetAssetsPath sets the assetsPath.
func (s *Settings) SetAssetsPath(assetsPath string) {
s.assetsPath = assetsPath
}
// SetTemplatesPath sets the templatesPath.
func (s *Settings) SetTemplatesPath(templatesPath string) {
s.templatesPath = templatesPath
}
// SetVersion sets the version.
func (s *Settings) SetVersion(version string) {
s.version = version
}

View File

@ -39,7 +39,11 @@ func (a *App) SetEchoSettings() {
e.Use(middleware.Recover())
e.Use(session.Middleware(sessions.NewCookieStore([]byte(a.config.SessionCookieSecret))))
e.Use(session.Middleware(
sessions.NewCookieStore(
[]byte(a.setting.SessionCookieSecret()),
),
))
// e.Use(middleware.CSRF())
e.Use(middleware.CSRFWithConfig(middleware.CSRFConfig{

View File

@ -3,37 +3,27 @@ package handlers
import (
"io/fs"
"git.dotya.ml/mirre-mt/pcmt/config"
"git.dotya.ml/mirre-mt/pcmt/app/settings"
"git.dotya.ml/mirre-mt/pcmt/slogging"
)
var (
conf *config.Config
setting *settings.Settings
appver string
log *slogging.Logger
tmplPath string
)
func setConfig(c *config.Config) {
if c != nil {
log.Info("setting handler config")
// c = conf
conf = c
log.Infof("set handler config to %#v", c)
} else {
log.Warn("error passing conf to handlers")
}
}
func setAppVer(v string) {
appver = v
}
func InitHandlers(version, templatesPath string, appconf *config.Config, tmpls fs.FS) {
func InitHandlers(s *settings.Settings, tmpls fs.FS) {
log = slogging.GetLogger()
tmplPath = templatesPath
setConfig(appconf)
setAppVer(version)
setting = s
tmplPath = setting.TemplatesPath()
appver = setting.Version()
log.Debugf("tmpls: %+v", tmpls)
initTemplates(tmpls)
}

View File

@ -92,7 +92,7 @@ func makeTplMap(tpl *template.Template) {
}
func getTmpl(name string) *template.Template {
liveMode := conf.LiveMode
liveMode := setting.IsLive()
if liveMode {
log.Info("re-reading tmpls") // TODO: only re-read the tmpl in question.
@ -123,11 +123,11 @@ func Index() echo.HandlerFunc {
err := tpl.Execute(c.Response().Writer,
page{
AppName: conf.AppName,
AppName: setting.AppName(),
AppVer: appver,
Title: "Welcome!",
CSRF: csrf,
DevelMode: conf.DevelMode,
DevelMode: setting.IsDevel(),
Current: "home",
},
)
@ -142,7 +142,7 @@ func Index() echo.HandlerFunc {
func Signin() echo.HandlerFunc {
return func(c echo.Context) error {
sess, _ := session.Get(conf.SessionCookieName, c)
sess, _ := session.Get(setting.SessionCookieName(), c)
username := sess.Values["username"]
if username != nil {
@ -153,10 +153,10 @@ func Signin() echo.HandlerFunc {
err := tpl.Execute(c.Response().Writer,
page{
AppName: conf.AppName,
AppName: setting.AppName(),
AppVer: appver,
Title: "Sign in",
DevelMode: conf.DevelMode,
DevelMode: setting.IsDevel(),
Current: "signin",
},
)
@ -222,7 +222,7 @@ func SigninPost(client *ent.Client) echo.HandlerFunc {
secure := c.Request().URL.Scheme == "https" //nolint:goconst
sess, _ := session.Get(conf.SessionCookieName, c)
sess, _ := session.Get(setting.SessionCookieName(), c)
if sess != nil {
sess.Options = &sessions.Options{
Path: "/",
@ -257,7 +257,7 @@ func SigninPost(client *ent.Client) echo.HandlerFunc {
func Signup() echo.HandlerFunc {
return func(c echo.Context) error {
sess, _ := session.Get(conf.SessionCookieName, c)
sess, _ := session.Get(setting.SessionCookieName(), c)
if sess != nil {
log.Info("gorilla session", "endpoint", "signup")
@ -285,11 +285,11 @@ func Signup() echo.HandlerFunc {
err := tpl.Execute(c.Response().Writer,
page{
AppName: conf.AppName,
AppName: setting.AppName(),
AppVer: appver,
Title: "Sign up",
CSRF: csrf,
DevelMode: conf.DevelMode,
DevelMode: setting.IsDevel(),
Current: "signup",
},
)
@ -398,7 +398,7 @@ func SignupPost(client *ent.Client) echo.HandlerFunc {
secure := c.Request().URL.Scheme == "https" //nolint:goconst
sess, _ := session.Get(conf.SessionCookieName, c)
sess, _ := session.Get(setting.SessionCookieName(), c)
sess.Options = &sessions.Options{
Path: "/",
MaxAge: 3600,
@ -435,7 +435,7 @@ func Home() echo.HandlerFunc {
tpl := getTmpl("home.tmpl")
sess, _ := session.Get(conf.SessionCookieName, c)
sess, _ := session.Get(setting.SessionCookieName(), c)
if sess == nil {
log.Info("no session, redirecting to /signin", "endpoint", "/home")
return c.Redirect(http.StatusPermanentRedirect, "/signin")
@ -463,11 +463,11 @@ func Home() echo.HandlerFunc {
err := tpl.Execute(c.Response().Writer,
page{
AppName: conf.AppName,
AppName: setting.AppName(),
AppVer: appver,
Title: "Home",
Name: username,
DevelMode: conf.DevelMode,
DevelMode: setting.IsDevel(),
Current: "home",
},
)
@ -500,7 +500,7 @@ func Logout() echo.HandlerFunc {
return func(c echo.Context) error {
switch {
case c.Request().Method == "POST":
sess, _ := session.Get(conf.SessionCookieName, c)
sess, _ := session.Get(setting.SessionCookieName(), c)
if sess != nil {
log.Infof("max-age before logout: %d", sess.Options.MaxAge)
sess.Options.MaxAge = -1
@ -522,10 +522,10 @@ func Logout() echo.HandlerFunc {
err := tpl.Execute(c.Response().Writer,
page{
AppName: conf.AppName,
AppName: setting.AppName(),
AppVer: appver,
Title: "Logout",
DevelMode: conf.DevelMode,
DevelMode: setting.IsDevel(),
Current: "logout",
},
)

View File

@ -12,10 +12,10 @@ func renderErrorPage(wr io.Writer, status int, statusText, error string) error {
err := tpl.Execute(wr,
page{
AppName: conf.AppName,
AppName: setting.AppName(),
AppVer: appver,
Title: fmt.Sprintf("Error %s - %s", strStatus, statusText),
DevelMode: conf.DevelMode,
DevelMode: setting.IsDevel(),
Current: strStatus,
Error: error,
Status: strStatus,

25
run.go
View File

@ -15,6 +15,7 @@ import (
_ "github.com/xiaoqidun/entps"
"git.dotya.ml/mirre-mt/pcmt/app"
"git.dotya.ml/mirre-mt/pcmt/app/settings"
"git.dotya.ml/mirre-mt/pcmt/config"
"git.dotya.ml/mirre-mt/pcmt/ent"
"git.dotya.ml/mirre-mt/pcmt/slogging"
@ -103,17 +104,19 @@ func run() error {
return fmt.Errorf("failed creating schema resources: %v", err)
}
setting := settings.New()
setting.Consolidate(
conf, host, port, devel, version,
)
a := &app.App{}
err = a.Init(version, log, conf, db)
err = a.Init(setting, log, db)
if err != nil {
return err
}
if *devel {
a.SetDevel()
}
a.PrintConfiguration()
a.SetupRoutes()
@ -138,16 +141,8 @@ func run() error {
defer close(started)
go func(ok chan error) {
p := conf.Port
h := conf.Host
if flagPort := *port; flagPort != 0 && flagPort > 0 && flagPort < 65536 {
p = flagPort
}
if flagHost := *host; flagHost != "unset" {
h = flagHost
}
p := setting.Port()
h := setting.Host()
address := h + ":" + strconv.Itoa(p)

View File

@ -1,3 +0,0 @@
package main
// func