Simplify configuration initialization

This commit is contained in:
Jan-Lukas Else 2020-08-04 19:42:09 +02:00
parent e3b346acf7
commit 31591d694c
7 changed files with 60 additions and 93 deletions

View File

@ -13,7 +13,7 @@ func cacheMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestUrl, _ := url.ParseRequestURI(r.RequestURI) requestUrl, _ := url.ParseRequestURI(r.RequestURI)
path := slashTrimmedPath(r) path := slashTrimmedPath(r)
if appConfig.cache.enable && if appConfig.Cache.Enable &&
// check bypass query // check bypass query
!(requestUrl != nil && requestUrl.Query().Get("cache") == "0") { !(requestUrl != nil && requestUrl.Query().Get("cache") == "0") {
cacheTime, header, body := getCache(r.Context(), path) cacheTime, header, body := getCache(r.Context(), path)
@ -27,7 +27,7 @@ func cacheMiddleware(next http.Handler) http.Handler {
w.Header()[k] = v w.Header()[k] = v
} }
now := time.Now() now := time.Now()
setCacheHeaders(w, now.Format(time.RFC1123), time.Unix(now.Unix()+appConfig.cache.expiration, 0).Format(time.RFC1123)) setCacheHeaders(w, now.Format(time.RFC1123), time.Unix(now.Unix()+appConfig.Cache.Expiration, 0).Format(time.RFC1123))
w.Header().Set("GoBlog-Cache", "MISS") w.Header().Set("GoBlog-Cache", "MISS")
w.WriteHeader(code) w.WriteHeader(code)
_, _ = w.Write(recorder.Body.Bytes()) _, _ = w.Write(recorder.Body.Bytes())
@ -38,7 +38,7 @@ func cacheMiddleware(next http.Handler) http.Handler {
return return
} }
cacheTimeString := time.Unix(cacheTime, 0).Format(time.RFC1123) cacheTimeString := time.Unix(cacheTime, 0).Format(time.RFC1123)
expiresTimeString := time.Unix(cacheTime+appConfig.cache.expiration, 0).Format(time.RFC1123) expiresTimeString := time.Unix(cacheTime+appConfig.Cache.Expiration, 0).Format(time.RFC1123)
// check conditional request // check conditional request
ifModifiedSinceHeader := r.Header.Get("If-Modified-Since") ifModifiedSinceHeader := r.Header.Get("If-Modified-Since")
if ifModifiedSinceHeader != "" && ifModifiedSinceHeader == cacheTimeString { if ifModifiedSinceHeader != "" && ifModifiedSinceHeader == cacheTimeString {
@ -70,7 +70,7 @@ func setCacheHeaders(w http.ResponseWriter, cacheTimeString string, expiresTimeS
func getCache(context context.Context, path string) (creationTime int64, header map[string][]string, body []byte) { func getCache(context context.Context, path string) (creationTime int64, header map[string][]string, body []byte) {
var headerBytes []byte var headerBytes []byte
allowedTime := time.Now().Unix() - appConfig.cache.expiration allowedTime := time.Now().Unix() - appConfig.Cache.Expiration
row := appDb.QueryRowContext(context, "select COALESCE(time, 0), header, body from cache where path=? and time>=?", path, allowedTime) row := appDb.QueryRowContext(context, "select COALESCE(time, 0), header, body from cache where path=? and time>=?", path, allowedTime)
_ = row.Scan(&creationTime, &headerBytes, &body) _ = row.Scan(&creationTime, &headerBytes, &body)
header = make(map[string][]string) header = make(map[string][]string)
@ -85,7 +85,7 @@ func saveCache(path string, now time.Time, header map[string][]string, body []by
if err != nil { if err != nil {
return return
} }
_, _ = tx.Exec("delete from cache where time<?;", now.Unix()-appConfig.cache.expiration) _, _ = tx.Exec("delete from cache where time<?;", now.Unix()-appConfig.Cache.Expiration)
_, _ = tx.Exec("insert or replace into cache (path, time, header, body) values (?, ?, ?, ?);", path, now.Unix(), headerBytes, body) _, _ = tx.Exec("insert or replace into cache (path, time, header, body) values (?, ?, ?, ?);", path, now.Unix(), headerBytes, body)
_ = tx.Commit() _ = tx.Commit()
finishWritingToDb() finishWritingToDb()

113
config.go
View File

@ -5,52 +5,46 @@ import (
) )
type config struct { type config struct {
server *configServer Server *configServer `mapstructure:"server"`
db *configDb Db *configDb `mapstructure:"database"`
cache *configCache Cache *configCache `mapstructure:"cache"`
blog *configBlog Blog *configBlog `mapstructure:"blog"`
user *configUser User *configUser `mapstructure:"user"`
} }
type configServer struct { type configServer struct {
logging bool Logging bool `mapstructure:"logging"`
port int Port int `mapstructure:"port"`
domain string Domain string `mapstructure:"domain"`
publicHttps bool PublicHttps bool `mapstructure:"publicHttps"`
letsEncryptMail string LetsEncryptMail string `mapstructure:"letsEncryptMail"`
localHttps bool LocalHttps bool `mapstructure:"localHttps"`
} }
type configDb struct { type configDb struct {
file string File string `mapstructure:"file"`
} }
type configCache struct { type configCache struct {
enable bool Enable bool `mapstructure:"enable"`
expiration int64 Expiration int64 `mapstructure:"expiration"`
} }
// exposed to templates via function "blog" // exposed to templates via function "blog"
type configBlog struct { type configBlog struct {
// Language of the blog, e.g. "en" or "de" // Language of the blog, e.g. "en" or "de"
Lang string Lang string `mapstructure:"lang"`
// Title of the blog, e.g. "My blog" // Title of the blog, e.g. "My blog"
Title string Title string `mapstructure:"title"`
} }
type configUser struct { type configUser struct {
nick string Nick string `mapstructure:"nick"`
name string Name string `mapstructure:"name"`
password string Password string `mapstructure:"password"`
} }
var appConfig = &config{ var appConfig = &config{}
server: &configServer{},
db: &configDb{},
cache: &configCache{},
blog: &configBlog{},
user: &configUser{},
}
func initConfig() error { func initConfig() error {
viper.SetConfigName("config") viper.SetConfigName("config")
@ -59,52 +53,25 @@ func initConfig() error {
if err != nil { if err != nil {
return err return err
} }
// Server // Defaults
serverLogging := "server.logging" viper.SetDefault("server.logging", false)
viper.SetDefault(serverLogging, false) viper.SetDefault("server.port", 8080)
appConfig.server.logging = viper.GetBool(serverLogging) viper.SetDefault("server.domain", "example.com")
serverPort := "server.port" viper.SetDefault("server.publicHttps", false)
viper.SetDefault(serverPort, 8080) viper.SetDefault("server.letsEncryptMail", "mail@example.com")
appConfig.server.port = viper.GetInt(serverPort) viper.SetDefault("server.localHttps", false)
serverDomain := "server.domain" viper.SetDefault("database.file", "data/db.sqlite")
viper.SetDefault(serverDomain, "example.com") viper.SetDefault("cache.enable", true)
appConfig.server.domain = viper.GetString(serverDomain) viper.SetDefault("cache.expiration", 600)
serverPublicHttps := "server.publicHttps" viper.SetDefault("blog.lang", "en")
viper.SetDefault(serverPublicHttps, false) viper.SetDefault("blog.title", "My blog")
appConfig.server.publicHttps = viper.GetBool(serverPublicHttps) viper.SetDefault("user.nick", "admin")
serverLetsEncryptMail := "server.letsEncryptMail" viper.SetDefault("user.name", "Admin")
viper.SetDefault(serverLetsEncryptMail, "mail@example.com") viper.SetDefault("user.password", "secret")
appConfig.server.letsEncryptMail = viper.GetString(serverLetsEncryptMail) // Unmarshal config
serverLocalHttps := "server.localHttps" err = viper.Unmarshal(appConfig)
viper.SetDefault(serverLocalHttps, false) if err != nil {
appConfig.server.localHttps = viper.GetBool(serverLocalHttps) return err
// Database }
databaseFile := "database.file"
viper.SetDefault(databaseFile, "data/db.sqlite")
appConfig.db.file = viper.GetString(databaseFile)
// Caching
cacheEnable := "cache.enable"
viper.SetDefault(cacheEnable, true)
appConfig.cache.enable = viper.GetBool(cacheEnable)
cacheExpiration := "cache.expiration"
viper.SetDefault(cacheExpiration, 600)
appConfig.cache.expiration = viper.GetInt64(cacheExpiration)
// Blog meta
blogLang := "blog.lang"
viper.SetDefault(blogLang, "en")
appConfig.blog.Lang = viper.GetString(blogLang)
blogTitle := "blog.title"
viper.SetDefault(blogTitle, "My blog")
appConfig.blog.Title = viper.GetString(blogTitle)
// User
userNick := "user.nick"
viper.SetDefault(userNick, "admin")
appConfig.user.nick = viper.GetString(userNick)
userName := "user.name"
viper.SetDefault(userName, "Admin")
appConfig.user.name = viper.GetString(userName)
userPassword := "user.password"
viper.SetDefault(userPassword, "secret")
appConfig.user.password = viper.GetString(userPassword)
return nil return nil
} }

View File

@ -10,7 +10,7 @@ var appDb *sql.DB
var appDbWriteMutex = &sync.Mutex{} var appDbWriteMutex = &sync.Mutex{}
func initDatabase() (err error) { func initDatabase() (err error) {
appDb, err = sql.Open("sqlite3", appConfig.db.file+"?cache=shared&mode=rwc&_journal_mode=WAL") appDb, err = sql.Open("sqlite3", appConfig.Db.File+"?cache=shared&mode=rwc&_journal_mode=WAL")
if err != nil { if err != nil {
return err return err
} }

4
go.mod
View File

@ -20,7 +20,7 @@ require (
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/pelletier/go-toml v1.8.0 // indirect github.com/pelletier/go-toml v1.8.0 // indirect
github.com/smartystreets/assertions v1.1.1 // indirect github.com/smartystreets/assertions v1.1.1 // indirect
github.com/spf13/afero v1.3.2 // indirect github.com/spf13/afero v1.3.3 // indirect
github.com/spf13/cast v1.3.1 // indirect github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/viper v1.7.1 github.com/spf13/viper v1.7.1
@ -30,7 +30,7 @@ require (
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de // indirect golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de // indirect
golang.org/x/net v0.0.0-20200707034311-ab3426394381 // indirect golang.org/x/net v0.0.0-20200707034311-ab3426394381 // indirect
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 // indirect golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 // indirect
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1 // indirect golang.org/x/sys v0.0.0-20200803210538-64077c9b5642 // indirect
golang.org/x/text v0.3.3 // indirect golang.org/x/text v0.3.3 // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
gopkg.in/ini.v1 v1.57.0 // indirect gopkg.in/ini.v1 v1.57.0 // indirect

8
go.sum
View File

@ -357,8 +357,8 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.3.2 h1:GDarE4TJQI52kYSbSAmLiId1Elfj+xgSDqrUZxFhxlU= github.com/spf13/afero v1.3.3 h1:p5gZEKLYoL7wh8VrJesMaYeNxdEd1v3cb4irOk9zB54=
github.com/spf13/afero v1.3.2/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
@ -540,8 +540,8 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1 h1:sIky/MyNRSHTrdxfsiUSS4WIAMvInbeXljJz+jDjeYE= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642 h1:B6caxRw+hozq68X2MY7jEpZh/cr4/aHLv9xU8Kkadrw=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

14
http.go
View File

@ -21,11 +21,11 @@ func startServer() (err error) {
return return
} }
d.swapHandler(h) d.swapHandler(h)
localAddress := ":" + strconv.Itoa(appConfig.server.port) localAddress := ":" + strconv.Itoa(appConfig.Server.Port)
if appConfig.server.publicHttps { if appConfig.Server.PublicHttps {
initPublicHTTPS() initPublicHTTPS()
err = certmagic.HTTPS([]string{appConfig.server.domain}, d) err = certmagic.HTTPS([]string{appConfig.Server.Domain}, d)
} else if appConfig.server.localHttps { } else if appConfig.Server.LocalHttps {
err = http.ListenAndServeTLS(localAddress, "https/server.crt", "https/server.key", d) err = http.ListenAndServeTLS(localAddress, "https/server.crt", "https/server.key", d)
} else { } else {
err = http.ListenAndServe(localAddress, d) err = http.ListenAndServe(localAddress, d)
@ -36,7 +36,7 @@ func startServer() (err error) {
func initPublicHTTPS() { func initPublicHTTPS() {
certmagic.Default.Storage = &certmagic.FileStorage{Path: "certmagic"} certmagic.Default.Storage = &certmagic.FileStorage{Path: "certmagic"}
certmagic.DefaultACME.Agreed = true certmagic.DefaultACME.Agreed = true
certmagic.DefaultACME.Email = appConfig.server.letsEncryptMail certmagic.DefaultACME.Email = appConfig.Server.LetsEncryptMail
certmagic.DefaultACME.CA = certmagic.LetsEncryptProductionCA certmagic.DefaultACME.CA = certmagic.LetsEncryptProductionCA
} }
@ -52,7 +52,7 @@ func reloadRouter() error {
func buildHandler() (http.Handler, error) { func buildHandler() (http.Handler, error) {
r := chi.NewRouter() r := chi.NewRouter()
if appConfig.server.logging { if appConfig.Server.Logging {
r.Use(middleware.RealIP) r.Use(middleware.RealIP)
r.Use(middleware.Logger) r.Use(middleware.Logger)
} }
@ -62,7 +62,7 @@ func buildHandler() (http.Handler, error) {
r.Route("/api", func(apiRouter chi.Router) { r.Route("/api", func(apiRouter chi.Router) {
apiRouter.Use(middleware.BasicAuth("API", map[string]string{ apiRouter.Use(middleware.BasicAuth("API", map[string]string{
appConfig.user.nick: appConfig.user.password, appConfig.User.Nick: appConfig.User.Password,
})) }))
apiRouter.Post("/post", apiPostCreate) apiRouter.Post("/post", apiPostCreate)
apiRouter.Delete("/post", apiPostDelete) apiRouter.Delete("/post", apiPostDelete)

View File

@ -18,7 +18,7 @@ var templateFunctions template.FuncMap
func initRendering() { func initRendering() {
templateFunctions = template.FuncMap{ templateFunctions = template.FuncMap{
"blog": func() *configBlog { "blog": func() *configBlog {
return appConfig.blog return appConfig.Blog
}, },
"md": func(content string) template.HTML { "md": func(content string) template.HTML {
htmlContent, err := renderMarkdown(content) htmlContent, err := renderMarkdown(content)