From c4dd574fdb2be79730f82e1e17b12a23fe2ff2ab Mon Sep 17 00:00:00 2001 From: vednoc Date: Sun, 24 Mar 2024 19:57:16 +0100 Subject: [PATCH 01/21] feat(config): switch to a configuration file --- cmd/userstyles-ts/main.go | 2 +- cmd/userstyles-world/main.go | 14 +++++++-- handlers/api/callback.go | 2 +- handlers/core/proxy.go | 2 +- handlers/jwt/jwt.go | 2 +- handlers/review/remove.go | 2 +- handlers/style/ban.go | 2 +- handlers/style/bulkban.go | 2 +- handlers/style/promote.go | 4 +-- handlers/user/account.go | 2 +- handlers/user/ban.go | 2 +- handlers/user/login.go | 2 +- handlers/user/recover.go | 2 +- models/style.go | 4 +-- modules/config/config.go | 48 +++++++++++++++++++++++++++++-- modules/database/init/init.go | 2 +- modules/log/log.go | 2 +- modules/storage/style_api_test.go | 2 +- modules/templates/templates.go | 6 ++-- web/web.go | 8 +++--- 20 files changed, 81 insertions(+), 31 deletions(-) diff --git a/cmd/userstyles-ts/main.go b/cmd/userstyles-ts/main.go index af638108..de054e20 100644 --- a/cmd/userstyles-ts/main.go +++ b/cmd/userstyles-ts/main.go @@ -32,7 +32,7 @@ func main() { // TODO: Remove this code? if shouldWatch { // Ensure we're seeing the error messages in stdout. - config.Production = false + config.Config.Production = false log.Initialize() watch = &api.WatchMode{ OnRebuild: func(result api.BuildResult) { diff --git a/cmd/userstyles-world/main.go b/cmd/userstyles-world/main.go index 1b317f0d..474a9e0a 100644 --- a/cmd/userstyles-world/main.go +++ b/cmd/userstyles-world/main.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "net/http" "os" "os/signal" @@ -34,6 +35,12 @@ import ( ) func main() { + err := config.New("config.json") + if err != nil { + fmt.Printf("Failed to load config: %s\n", err) + os.Exit(1) + } + log.Initialize() cache.Initialize() images.CheckVips() @@ -41,6 +48,7 @@ func main() { validator.Init() database.Initialize() cron.Initialize() + web.Init() app := fiber.New(fiber.Config{ Views: templates.New(http.FS(web.ViewsDir)), @@ -55,7 +63,7 @@ func main() { email.SetRenderer(app) - if !config.Production { + if !config.Config.Production { app.Use(logger.New()) } @@ -91,7 +99,7 @@ func main() { })) // TODO: Investigate how to "truly" inline sourcemaps in Sass. - if !config.Production { + if !config.Config.Production { app.Static("/scss", "web/scss") } @@ -99,7 +107,7 @@ func main() { app.Use(core.NotFound) go func() { - if err := app.Listen(config.Port); err != nil { + if err := app.Listen(config.Config.Addr); err != nil { log.Warn.Fatal(err) } }() diff --git a/handlers/api/callback.go b/handlers/api/callback.go index 553e687e..e6749cd1 100644 --- a/handlers/api/callback.go +++ b/handlers/api/callback.go @@ -95,7 +95,7 @@ func CallbackGet(c *fiber.Ctx) error { Value: t, Path: "/", Expires: expiration, - Secure: config.Production, + Secure: config.Config.Production, HTTPOnly: true, SameSite: fiber.CookieSameSiteLaxMode, }) diff --git a/handlers/core/proxy.go b/handlers/core/proxy.go index c7a88d5f..fcb7865a 100644 --- a/handlers/core/proxy.go +++ b/handlers/core/proxy.go @@ -26,7 +26,7 @@ var client = http.Client{ } // Make sure it doesn't redirect to a loopback thingy. - if config.Production && util.IsLoopback(string(req.Host)) { + if config.Config.Production && util.IsLoopback(string(req.Host)) { return errors.New("*giggles* Mikey Wikey hates you") } return nil diff --git a/handlers/jwt/jwt.go b/handlers/jwt/jwt.go index 575b1a34..0b3495fa 100644 --- a/handlers/jwt/jwt.go +++ b/handlers/jwt/jwt.go @@ -33,7 +33,7 @@ var Protected = func(c *fiber.Ctx) error { var Admin = func(c *fiber.Ctx) error { // Bypass checks if monitor is enabled and request is a local IP address. - if config.PerformanceMonitor && util.IsLocal(config.Production, c.IP()) { + if config.PerformanceMonitor && util.IsLocal(config.Config.Production, c.IP()) { return c.Next() } diff --git a/handlers/review/remove.go b/handlers/review/remove.go index fd5456d8..8c380061 100644 --- a/handlers/review/remove.go +++ b/handlers/review/remove.go @@ -106,7 +106,7 @@ func removeForm(c *fiber.Ctx) error { args := fiber.Map{ "User": r.User, "Log": l, - "Link": config.BaseURL + "/modlog#id-" + strconv.Itoa(int(l.ID)), + "Link": config.Config.BaseURL + "/modlog#id-" + strconv.Itoa(int(l.ID)), } title := "Your review has been removed" diff --git a/handlers/style/ban.go b/handlers/style/ban.go index 9a5b24be..e8db7ca2 100644 --- a/handlers/style/ban.go +++ b/handlers/style/ban.go @@ -156,7 +156,7 @@ func sendRemovalEmail(user *storage.User, style *models.Style, event *models.Log "User": user, "Style": style, "Log": event, - "Link": config.BaseURL + "/modlog#id-" + strconv.Itoa(int(event.ID)), + "Link": config.Config.BaseURL + "/modlog#id-" + strconv.Itoa(int(event.ID)), } title := "Your style has been removed" diff --git a/handlers/style/bulkban.go b/handlers/style/bulkban.go index 55cfff44..54a07358 100644 --- a/handlers/style/bulkban.go +++ b/handlers/style/bulkban.go @@ -134,7 +134,7 @@ func sendBulkRemovalEmail(user *storage.User, styles []*models.Style, event *mod "User": user, "Styles": styles, "Log": event, - "Link": config.BaseURL + "/modlog#id-" + strconv.Itoa(int(event.ID)), + "Link": config.Config.BaseURL + "/modlog#id-" + strconv.Itoa(int(event.ID)), } var title string diff --git a/handlers/style/promote.go b/handlers/style/promote.go index 46efb8d8..9972c444 100644 --- a/handlers/style/promote.go +++ b/handlers/style/promote.go @@ -17,9 +17,9 @@ func sendPromotionEmail(style *models.APIStyle, user *models.User, mod string) { args := fiber.Map{ "User": user, "Style": style, - "StyleLink": config.BaseURL + "/style/" + strconv.Itoa(int(style.ID)), + "StyleLink": config.Config.BaseURL + "/style/" + strconv.Itoa(int(style.ID)), "ModName": mod, - "ModLink": config.BaseURL + "/user/" + mod, + "ModLink": config.Config.BaseURL + "/user/" + mod, } title := "Your style has been featured" diff --git a/handlers/user/account.go b/handlers/user/account.go index 00941b9d..d7fa1c25 100644 --- a/handlers/user/account.go +++ b/handlers/user/account.go @@ -176,7 +176,7 @@ func EditAccount(c *fiber.Ctx) error { Value: v, Path: "/", Expires: time.Now().Add(time.Hour * 24 * 30 * 6), - Secure: config.Production, + Secure: config.Config.Production, HTTPOnly: true, SameSite: fiber.CookieSameSiteLaxMode, } diff --git a/handlers/user/ban.go b/handlers/user/ban.go index 95e7f4b3..ae2777bb 100644 --- a/handlers/user/ban.go +++ b/handlers/user/ban.go @@ -117,7 +117,7 @@ func ConfirmBan(c *fiber.Ctx) error { args := fiber.Map{ "User": user, "Reason": logEntry.Reason, - "Link": config.BaseURL + "/modlog#id-" + strconv.Itoa(int(logEntry.ID)), + "Link": config.Config.BaseURL + "/modlog#id-" + strconv.Itoa(int(logEntry.ID)), } err = email.Send("user/ban", user.Email, "You have been banned", args) if err != nil { diff --git a/handlers/user/login.go b/handlers/user/login.go index 038aefaa..663c049f 100644 --- a/handlers/user/login.go +++ b/handlers/user/login.go @@ -102,7 +102,7 @@ func LoginPost(c *fiber.Ctx) error { Value: t, Path: "/", Expires: expiration, - Secure: config.Production, + Secure: config.Config.Production, HTTPOnly: true, SameSite: fiber.CookieSameSiteLaxMode, } diff --git a/handlers/user/recover.go b/handlers/user/recover.go index 580c09fe..f133081f 100644 --- a/handlers/user/recover.go +++ b/handlers/user/recover.go @@ -78,7 +78,7 @@ func RecoverPost(c *fiber.Ctx) error { return } - link := config.BaseURL + "/reset/" + util.EncryptText(jwtToken, util.AEADCrypto, config.ScrambleConfig) + link := config.Config.BaseURL + "/reset/" + util.EncryptText(jwtToken, util.AEADCrypto, config.ScrambleConfig) args := fiber.Map{ "User": user, diff --git a/models/style.go b/models/style.go index 6c15041a..2e2329d1 100644 --- a/models/style.go +++ b/models/style.go @@ -281,7 +281,7 @@ func (s *Style) UpdateColumn(col string, val any) error { // SetPreview will set preview image URL. func (s *Style) SetPreview() { - s.Preview = fmt.Sprintf("%s/preview/%d/%dt.webp", config.BaseURL, s.ID, s.PreviewVersion) + s.Preview = fmt.Sprintf("%s/preview/%d/%dt.webp", config.Config.BaseURL, s.ID, s.PreviewVersion) } var ( @@ -369,7 +369,7 @@ func (s Style) ValidateCode(v *validator.Validate, addPage bool) (string, error) // SetPreview will set preview image URL. func (s *APIStyle) SetPreview() { - s.Preview = fmt.Sprintf("%s/preview/%d/%dt.webp", config.BaseURL, s.ID, s.PreviewVersion) + s.Preview = fmt.Sprintf("%s/preview/%d/%dt.webp", config.Config.BaseURL, s.ID, s.PreviewVersion) } // SelectUpdateStyle will update specific fields in the styles table. diff --git a/modules/config/config.go b/modules/config/config.go index 1a9fed76..6e8eef49 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -1,12 +1,55 @@ package config import ( + "encoding/json" "fmt" + "os" "path" "strings" "time" ) +type config struct { + Debug bool + Production bool + Addr string + BaseURL string + DatabaseName string +} + +var Config *config + +var defaultConfig = &config{ + Addr: ":3000", + BaseURL: "http://localhost:3000", + DatabaseName: "dev.db", +} + +func New(path string) error { + b, err := os.ReadFile(path) + if err != nil { + return err + } + + c := defaultConfig + err = json.Unmarshal(b, &c) + if err != nil { + return err + } + + Config = c + + return nil +} + +func PrintConfig(c *config) (string, error) { + b, err := json.MarshalIndent(c, "", "\t") + if err != nil { + return "", err + } + return string(b), nil +} + type ScrambleSettings struct { StepSize int BytesPerInsert int @@ -16,8 +59,7 @@ var ( GitCommit string GitVersion string - Port = getEnv("PORT", ":3000") - BaseURL = getEnv("BASE_URL", "http://localhost"+Port) + // BaseURL = getEnv("BASE_URL", "http://localhost"+Port) DB = getEnv("DB", "dev.db") DBDebug = getEnv("DB_DEBUG", "silent") DBColor = getEnvBool("DB_COLOR", false) @@ -84,7 +126,7 @@ var ( // OAuthURL returns the proper callback URL depending on the environment. func OAuthURL() string { - return BaseURL + "/api/callback/" + return Config.BaseURL + "/api/callback/" } // raw tweaks allowed URLs to make them work seamlessly in both environments. diff --git a/modules/database/init/init.go b/modules/database/init/init.go index a5ca69a5..50f19b7d 100644 --- a/modules/database/init/init.go +++ b/modules/database/init/init.go @@ -79,7 +79,7 @@ func Initialize() { shouldSeed := false // Generate data for development. - if config.DBDrop && !config.Production { + if config.DBDrop && !config.Config.Production { for _, table := range tables { if err := drop(table.model); err != nil { log.Warn.Fatalf("Failed to drop %s, err: %s\n", table.name, err.Error()) diff --git a/modules/log/log.go b/modules/log/log.go index 4fb80a94..49edf353 100644 --- a/modules/log/log.go +++ b/modules/log/log.go @@ -17,7 +17,7 @@ var ( ) func setOutput(f *os.File) io.Writer { - if config.Production { + if config.Config.Production { return io.MultiWriter(f) } diff --git a/modules/storage/style_api_test.go b/modules/storage/style_api_test.go index 3417ad28..aa3ffa5a 100644 --- a/modules/storage/style_api_test.go +++ b/modules/storage/style_api_test.go @@ -96,7 +96,7 @@ func BenchmarkGetStyleCompactIndex(b *testing.B) { UpdatedAt: time.Date(1970, 1, 1, 1, 0, 0, 0, time.UTC), }, Name: "test " + id, - Preview: config.BaseURL + "/preview/" + id + "/0.webp", + Preview: config.Config.BaseURL + "/preview/" + id + "/0.webp", }) } diff --git a/modules/templates/templates.go b/modules/templates/templates.go index b715e580..474917e6 100644 --- a/modules/templates/templates.go +++ b/modules/templates/templates.go @@ -133,9 +133,9 @@ func New(views http.FileSystem) *html.Engine { engine.AddFunc("canonical", func(url any) template.HTML { if url == nil { - return template.HTML(config.BaseURL) + return template.HTML(config.Config.BaseURL) } - return template.HTML(config.BaseURL + "/" + url.(string)) + return template.HTML(config.Config.BaseURL + "/" + url.(string)) }) engine.AddFunc("Elapsed", func(dur time.Duration) template.HTML { @@ -160,7 +160,7 @@ func New(views http.FileSystem) *html.Engine { return string(b) }) - if !config.Production { + if !config.Config.Production { engine.Reload(true) } diff --git a/web/web.go b/web/web.go index 38296399..83d18e31 100644 --- a/web/web.go +++ b/web/web.go @@ -20,19 +20,19 @@ var ( ViewsDir fs.FS ) -func init() { +func Init() { var err error - DocsDir, err = util.EmbedFS(files, "web/docs", config.Production) + DocsDir, err = util.EmbedFS(files, "web/docs", config.Config.Production) if err != nil { log.Fatalf("Failed to set docs directory: %s\n", err) } - StaticDir, err = util.EmbedFS(files, "web/static", config.Production) + StaticDir, err = util.EmbedFS(files, "web/static", config.Config.Production) if err != nil { log.Fatalf("Failed to set static directory: %s\n", err) } - ViewsDir, err = util.EmbedFS(files, "web/views", config.Production) + ViewsDir, err = util.EmbedFS(files, "web/views", config.Config.Production) if err != nil { log.Fatalf("Failed to set views directory: %s\n", err) } From 07769a056d74602afb87573de23ad48aefc4af1d Mon Sep 17 00:00:00 2001 From: vednoc Date: Thu, 22 Aug 2024 18:26:32 +0200 Subject: [PATCH 02/21] feat(config): split config into multiple structs --- cmd/userstyles-ts/main.go | 2 +- cmd/userstyles-world/main.go | 8 ++-- handlers/api/callback.go | 2 +- handlers/core/proxy.go | 2 +- handlers/jwt/jwt.go | 2 +- handlers/review/remove.go | 2 +- handlers/style/ban.go | 2 +- handlers/style/bulkban.go | 2 +- handlers/style/promote.go | 4 +- handlers/user/account.go | 2 +- handlers/user/ban.go | 2 +- handlers/user/login.go | 2 +- handlers/user/recover.go | 2 +- models/style.go | 4 +- modules/config/config.go | 74 ++++++++++++++++++------------- modules/database/init/init.go | 2 +- modules/log/log.go | 2 +- modules/storage/style_api_test.go | 2 +- modules/templates/templates.go | 6 +-- web/web.go | 6 +-- 20 files changed, 70 insertions(+), 60 deletions(-) diff --git a/cmd/userstyles-ts/main.go b/cmd/userstyles-ts/main.go index de054e20..0df32e61 100644 --- a/cmd/userstyles-ts/main.go +++ b/cmd/userstyles-ts/main.go @@ -32,7 +32,7 @@ func main() { // TODO: Remove this code? if shouldWatch { // Ensure we're seeing the error messages in stdout. - config.Config.Production = false + config.App.Production = false log.Initialize() watch = &api.WatchMode{ OnRebuild: func(result api.BuildResult) { diff --git a/cmd/userstyles-world/main.go b/cmd/userstyles-world/main.go index 474a9e0a..4f8ccedb 100644 --- a/cmd/userstyles-world/main.go +++ b/cmd/userstyles-world/main.go @@ -35,7 +35,7 @@ import ( ) func main() { - err := config.New("config.json") + err := config.Load("config.json") if err != nil { fmt.Printf("Failed to load config: %s\n", err) os.Exit(1) @@ -63,7 +63,7 @@ func main() { email.SetRenderer(app) - if !config.Config.Production { + if !config.App.Production { app.Use(logger.New()) } @@ -99,7 +99,7 @@ func main() { })) // TODO: Investigate how to "truly" inline sourcemaps in Sass. - if !config.Config.Production { + if !config.App.Production { app.Static("/scss", "web/scss") } @@ -107,7 +107,7 @@ func main() { app.Use(core.NotFound) go func() { - if err := app.Listen(config.Config.Addr); err != nil { + if err := app.Listen(config.App.Addr); err != nil { log.Warn.Fatal(err) } }() diff --git a/handlers/api/callback.go b/handlers/api/callback.go index e6749cd1..a3b0fa0b 100644 --- a/handlers/api/callback.go +++ b/handlers/api/callback.go @@ -95,7 +95,7 @@ func CallbackGet(c *fiber.Ctx) error { Value: t, Path: "/", Expires: expiration, - Secure: config.Config.Production, + Secure: config.App.Production, HTTPOnly: true, SameSite: fiber.CookieSameSiteLaxMode, }) diff --git a/handlers/core/proxy.go b/handlers/core/proxy.go index fcb7865a..fc918fdb 100644 --- a/handlers/core/proxy.go +++ b/handlers/core/proxy.go @@ -26,7 +26,7 @@ var client = http.Client{ } // Make sure it doesn't redirect to a loopback thingy. - if config.Config.Production && util.IsLoopback(string(req.Host)) { + if config.App.Production && util.IsLoopback(string(req.Host)) { return errors.New("*giggles* Mikey Wikey hates you") } return nil diff --git a/handlers/jwt/jwt.go b/handlers/jwt/jwt.go index 0b3495fa..feadee96 100644 --- a/handlers/jwt/jwt.go +++ b/handlers/jwt/jwt.go @@ -33,7 +33,7 @@ var Protected = func(c *fiber.Ctx) error { var Admin = func(c *fiber.Ctx) error { // Bypass checks if monitor is enabled and request is a local IP address. - if config.PerformanceMonitor && util.IsLocal(config.Config.Production, c.IP()) { + if config.PerformanceMonitor && util.IsLocal(config.App.Production, c.IP()) { return c.Next() } diff --git a/handlers/review/remove.go b/handlers/review/remove.go index 8c380061..a2b5df65 100644 --- a/handlers/review/remove.go +++ b/handlers/review/remove.go @@ -106,7 +106,7 @@ func removeForm(c *fiber.Ctx) error { args := fiber.Map{ "User": r.User, "Log": l, - "Link": config.Config.BaseURL + "/modlog#id-" + strconv.Itoa(int(l.ID)), + "Link": config.App.BaseURL + "/modlog#id-" + strconv.Itoa(int(l.ID)), } title := "Your review has been removed" diff --git a/handlers/style/ban.go b/handlers/style/ban.go index e8db7ca2..b24b0d2c 100644 --- a/handlers/style/ban.go +++ b/handlers/style/ban.go @@ -156,7 +156,7 @@ func sendRemovalEmail(user *storage.User, style *models.Style, event *models.Log "User": user, "Style": style, "Log": event, - "Link": config.Config.BaseURL + "/modlog#id-" + strconv.Itoa(int(event.ID)), + "Link": config.App.BaseURL + "/modlog#id-" + strconv.Itoa(int(event.ID)), } title := "Your style has been removed" diff --git a/handlers/style/bulkban.go b/handlers/style/bulkban.go index 54a07358..326cf862 100644 --- a/handlers/style/bulkban.go +++ b/handlers/style/bulkban.go @@ -134,7 +134,7 @@ func sendBulkRemovalEmail(user *storage.User, styles []*models.Style, event *mod "User": user, "Styles": styles, "Log": event, - "Link": config.Config.BaseURL + "/modlog#id-" + strconv.Itoa(int(event.ID)), + "Link": config.App.BaseURL + "/modlog#id-" + strconv.Itoa(int(event.ID)), } var title string diff --git a/handlers/style/promote.go b/handlers/style/promote.go index 9972c444..e8d1dd3c 100644 --- a/handlers/style/promote.go +++ b/handlers/style/promote.go @@ -17,9 +17,9 @@ func sendPromotionEmail(style *models.APIStyle, user *models.User, mod string) { args := fiber.Map{ "User": user, "Style": style, - "StyleLink": config.Config.BaseURL + "/style/" + strconv.Itoa(int(style.ID)), + "StyleLink": config.App.BaseURL + "/style/" + strconv.Itoa(int(style.ID)), "ModName": mod, - "ModLink": config.Config.BaseURL + "/user/" + mod, + "ModLink": config.App.BaseURL + "/user/" + mod, } title := "Your style has been featured" diff --git a/handlers/user/account.go b/handlers/user/account.go index d7fa1c25..2091f754 100644 --- a/handlers/user/account.go +++ b/handlers/user/account.go @@ -176,7 +176,7 @@ func EditAccount(c *fiber.Ctx) error { Value: v, Path: "/", Expires: time.Now().Add(time.Hour * 24 * 30 * 6), - Secure: config.Config.Production, + Secure: config.App.Production, HTTPOnly: true, SameSite: fiber.CookieSameSiteLaxMode, } diff --git a/handlers/user/ban.go b/handlers/user/ban.go index ae2777bb..2efc6838 100644 --- a/handlers/user/ban.go +++ b/handlers/user/ban.go @@ -117,7 +117,7 @@ func ConfirmBan(c *fiber.Ctx) error { args := fiber.Map{ "User": user, "Reason": logEntry.Reason, - "Link": config.Config.BaseURL + "/modlog#id-" + strconv.Itoa(int(logEntry.ID)), + "Link": config.App.BaseURL + "/modlog#id-" + strconv.Itoa(int(logEntry.ID)), } err = email.Send("user/ban", user.Email, "You have been banned", args) if err != nil { diff --git a/handlers/user/login.go b/handlers/user/login.go index 663c049f..b4834c67 100644 --- a/handlers/user/login.go +++ b/handlers/user/login.go @@ -102,7 +102,7 @@ func LoginPost(c *fiber.Ctx) error { Value: t, Path: "/", Expires: expiration, - Secure: config.Config.Production, + Secure: config.App.Production, HTTPOnly: true, SameSite: fiber.CookieSameSiteLaxMode, } diff --git a/handlers/user/recover.go b/handlers/user/recover.go index f133081f..dca15f3b 100644 --- a/handlers/user/recover.go +++ b/handlers/user/recover.go @@ -78,7 +78,7 @@ func RecoverPost(c *fiber.Ctx) error { return } - link := config.Config.BaseURL + "/reset/" + util.EncryptText(jwtToken, util.AEADCrypto, config.ScrambleConfig) + link := config.App.BaseURL + "/reset/" + util.EncryptText(jwtToken, util.AEADCrypto, config.ScrambleConfig) args := fiber.Map{ "User": user, diff --git a/models/style.go b/models/style.go index 2e2329d1..702e2b09 100644 --- a/models/style.go +++ b/models/style.go @@ -281,7 +281,7 @@ func (s *Style) UpdateColumn(col string, val any) error { // SetPreview will set preview image URL. func (s *Style) SetPreview() { - s.Preview = fmt.Sprintf("%s/preview/%d/%dt.webp", config.Config.BaseURL, s.ID, s.PreviewVersion) + s.Preview = fmt.Sprintf("%s/preview/%d/%dt.webp", config.App.BaseURL, s.ID, s.PreviewVersion) } var ( @@ -369,7 +369,7 @@ func (s Style) ValidateCode(v *validator.Validate, addPage bool) (string, error) // SetPreview will set preview image URL. func (s *APIStyle) SetPreview() { - s.Preview = fmt.Sprintf("%s/preview/%d/%dt.webp", config.Config.BaseURL, s.ID, s.PreviewVersion) + s.Preview = fmt.Sprintf("%s/preview/%d/%dt.webp", config.App.BaseURL, s.ID, s.PreviewVersion) } // SelectUpdateStyle will update specific fields in the styles table. diff --git a/modules/config/config.go b/modules/config/config.go index 6e8eef49..825fb936 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -1,53 +1,67 @@ +// Package config provides configuration options. package config import ( "encoding/json" "fmt" + "log" "os" "path" "strings" "time" ) -type config struct { - Debug bool - Production bool - Addr string - BaseURL string - DatabaseName string -} +type ( + AppConfig struct { + Debug bool + Production bool + Addr string + BaseURL string + } -var Config *config + config struct { + App AppConfig + } +) -var defaultConfig = &config{ - Addr: ":3000", - BaseURL: "http://localhost:3000", - DatabaseName: "dev.db", +var ( + // App stores general configuration. + App *AppConfig +) + +func defaultConfig() *config { + return &config{ + App: AppConfig{ + Addr: ":3000", + BaseURL: "http://localhost:3000", + }, + } } -func New(path string) error { +// Load tries to load configuration from a given path. +func Load(path string) error { b, err := os.ReadFile(path) if err != nil { return err } - c := defaultConfig - err = json.Unmarshal(b, &c) - if err != nil { + c := defaultConfig() + if err = json.Unmarshal(b, &c); err != nil { return err } - Config = c + if c.App.Debug { + b, err := json.MarshalIndent(c, "", "\t") + if err != nil { + return err + } - return nil -} - -func PrintConfig(c *config) (string, error) { - b, err := json.MarshalIndent(c, "", "\t") - if err != nil { - return "", err + log.Println("config:", string(b)) } - return string(b), nil + + App = &c.App + + return nil } type ScrambleSettings struct { @@ -59,7 +73,6 @@ var ( GitCommit string GitVersion string - // BaseURL = getEnv("BASE_URL", "http://localhost"+Port) DB = getEnv("DB", "dev.db") DBDebug = getEnv("DB_DEBUG", "silent") DBColor = getEnvBool("DB_COLOR", false) @@ -89,9 +102,6 @@ var ( ProxyMonitor = getEnv("PROXY_MONITOR", "unset") SearchReindex = getEnvBool("SEARCH_REINDEX", false) - // Production is used for various "feature flags". - Production = DB != "dev.db" - ScrambleConfig = &ScrambleSettings{ StepSize: getEnvInt("NONCE_SCRAMBLE_STEP", 2), BytesPerInsert: getEnvInt("NONCE_SCRAMBLE_BYTES_PER_INSERT", 3), @@ -124,14 +134,14 @@ var ( ProxyRealIP = getEnv("PROXY_REAL_IP", "") ) -// OAuthURL returns the proper callback URL depending on the environment. +// OAuthURL returns an environment-specific callback URL used for OAuth services. func OAuthURL() string { - return Config.BaseURL + "/api/callback/" + return App.BaseURL + "/api/callback/" } // raw tweaks allowed URLs to make them work seamlessly in both environments. func raw(s string) string { - if !Production { + if !App.Production { s += "|userstyles.world" } r := strings.NewReplacer("http://", "", "https://", "") diff --git a/modules/database/init/init.go b/modules/database/init/init.go index 50f19b7d..7a5a2e1b 100644 --- a/modules/database/init/init.go +++ b/modules/database/init/init.go @@ -79,7 +79,7 @@ func Initialize() { shouldSeed := false // Generate data for development. - if config.DBDrop && !config.Config.Production { + if config.DBDrop && !config.App.Production { for _, table := range tables { if err := drop(table.model); err != nil { log.Warn.Fatalf("Failed to drop %s, err: %s\n", table.name, err.Error()) diff --git a/modules/log/log.go b/modules/log/log.go index 49edf353..d63f0232 100644 --- a/modules/log/log.go +++ b/modules/log/log.go @@ -17,7 +17,7 @@ var ( ) func setOutput(f *os.File) io.Writer { - if config.Config.Production { + if config.App.Production { return io.MultiWriter(f) } diff --git a/modules/storage/style_api_test.go b/modules/storage/style_api_test.go index aa3ffa5a..c84200d9 100644 --- a/modules/storage/style_api_test.go +++ b/modules/storage/style_api_test.go @@ -96,7 +96,7 @@ func BenchmarkGetStyleCompactIndex(b *testing.B) { UpdatedAt: time.Date(1970, 1, 1, 1, 0, 0, 0, time.UTC), }, Name: "test " + id, - Preview: config.Config.BaseURL + "/preview/" + id + "/0.webp", + Preview: config.App.BaseURL + "/preview/" + id + "/0.webp", }) } diff --git a/modules/templates/templates.go b/modules/templates/templates.go index 474917e6..f702f023 100644 --- a/modules/templates/templates.go +++ b/modules/templates/templates.go @@ -133,9 +133,9 @@ func New(views http.FileSystem) *html.Engine { engine.AddFunc("canonical", func(url any) template.HTML { if url == nil { - return template.HTML(config.Config.BaseURL) + return template.HTML(config.App.BaseURL) } - return template.HTML(config.Config.BaseURL + "/" + url.(string)) + return template.HTML(config.App.BaseURL + "/" + url.(string)) }) engine.AddFunc("Elapsed", func(dur time.Duration) template.HTML { @@ -160,7 +160,7 @@ func New(views http.FileSystem) *html.Engine { return string(b) }) - if !config.Config.Production { + if !config.App.Production { engine.Reload(true) } diff --git a/web/web.go b/web/web.go index 83d18e31..b1e971e1 100644 --- a/web/web.go +++ b/web/web.go @@ -22,17 +22,17 @@ var ( func Init() { var err error - DocsDir, err = util.EmbedFS(files, "web/docs", config.Config.Production) + DocsDir, err = util.EmbedFS(files, "web/docs", config.App.Production) if err != nil { log.Fatalf("Failed to set docs directory: %s\n", err) } - StaticDir, err = util.EmbedFS(files, "web/static", config.Config.Production) + StaticDir, err = util.EmbedFS(files, "web/static", config.App.Production) if err != nil { log.Fatalf("Failed to set static directory: %s\n", err) } - ViewsDir, err = util.EmbedFS(files, "web/views", config.Config.Production) + ViewsDir, err = util.EmbedFS(files, "web/views", config.App.Production) if err != nil { log.Fatalf("Failed to set views directory: %s\n", err) } From 9cbe0be26e135fe20ea88ed0b255f39739373f24 Mon Sep 17 00:00:00 2001 From: vednoc Date: Thu, 22 Aug 2024 19:17:48 +0200 Subject: [PATCH 03/21] feat(build): embed build info in dev executable --- tools/run | 26 ++++++++++++++++++-------- web/views/partials/footer.tmpl | 10 +++------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/tools/run b/tools/run index 9d887809..9ef56a3e 100755 --- a/tools/run +++ b/tools/run @@ -42,6 +42,14 @@ check() { # Jobs. build() { + : "${flags:=""}" + : "${config:="userstyles.world/modules/config"}" + + setBuildInfo() { + flags="$flags -X ${config}.GitCommit=$(git rev-list -1 HEAD)" + flags="$flags -X ${config}.GitVersion=$(git describe --tags)" + } + case "$1" in fonts) log "Downloading fonts." @@ -50,21 +58,23 @@ build() { go-dev) : "Setting BIN to ${BIN:=bin/userstyles-dev}." log "Compiling development executable to ${BIN}." - go build -v -o "$BIN" -tags "fts5" cmd/userstyles-world/main.go + + setBuildInfo + go build -v \ + -o "$BIN" \ + -ldflags "$flags" \ + -tags "fts5" \ + cmd/userstyles-world/main.go ;; go-prod) : "Setting BIN to ${BIN:=bin/userstyles-prod}." log "Compiling production executable to ${BIN}." - c="$(git rev-list -1 HEAD)" - v="$(git describe --tags)" - f="-s -w -extldflags '-fno-PIC -static'" - f="$f -X userstyles.world/modules/config.GitCommit=${c}" - f="$f -X userstyles.world/modules/config.GitVersion=${v}" - + flags="-s -w -extldflags '-fno-PIC -static'" + setBuildInfo go build -v \ -o "$BIN" \ - -ldflags "$f" \ + -ldflags "$flags" \ -buildmode pie \ -tags 'osusergo netgo static_build fts5' \ cmd/userstyles-world/main.go diff --git a/web/views/partials/footer.tmpl b/web/views/partials/footer.tmpl index 5223377f..a065e867 100644 --- a/web/views/partials/footer.tmpl +++ b/web/views/partials/footer.tmpl @@ -7,13 +7,9 @@
  • © 2020–{{ config "copyright" }} {{ config "appName" }}
  • - {{ if (config "appVersion") }} - - {{- printf "%s (%s)" (config "appVersion") (config "appCodeName") -}} - - {{ else }} - DevEnv - {{ end }} + + {{ config "appVersion" }} ({{ config "appCodeName" }}) +
  • From 5b0023b8fc86140ecdcaa0753e11e46a4f60100e Mon Sep 17 00:00:00 2001 From: vednoc Date: Fri, 30 Aug 2024 20:09:22 +0200 Subject: [PATCH 04/21] feat(config): rework variables used in templates --- cmd/userstyles-world/main.go | 4 +++ handlers/core/dashboard.go | 2 +- handlers/core/link.go | 8 ++--- handlers/style/category.go | 2 +- handlers/style/explore.go | 2 +- handlers/user/profile.go | 2 +- models/pagination.go | 4 +-- modules/config/config.go | 54 +++++++++++++++++++++++---------- modules/database/init/utils.go | 2 +- modules/storage/style_search.go | 4 +-- modules/templates/templates.go | 17 +---------- web/views/core/home.tmpl | 2 +- web/views/partials/footer.tmpl | 17 +++-------- web/views/partials/head.tmpl | 8 ++--- web/views/partials/nav.tmpl | 2 +- web/views/partials/sidebar.tmpl | 2 +- web/views/user/account.tmpl | 2 +- web/views/user/login.tmpl | 2 +- web/views/user/recover.tmpl | 2 +- web/views/user/register.tmpl | 2 +- 20 files changed, 72 insertions(+), 68 deletions(-) diff --git a/cmd/userstyles-world/main.go b/cmd/userstyles-world/main.go index 4f8ccedb..e8ef6887 100644 --- a/cmd/userstyles-world/main.go +++ b/cmd/userstyles-world/main.go @@ -69,6 +69,10 @@ func main() { api.FastRoutes(app) + app.Use(func(c *fiber.Ctx) error { + c.Locals("App", &config.App) + return c.Next() + }) app.Use(core.HSTSMiddleware) app.Use(core.CSPMiddleware) app.Use(core.FlagsMiddleware) diff --git a/handlers/core/dashboard.go b/handlers/core/dashboard.go index e913696c..0866ebc6 100644 --- a/handlers/core/dashboard.go +++ b/handlers/core/dashboard.go @@ -61,7 +61,7 @@ func getSystemStatus() { systemMutex.Lock() defer systemMutex.Unlock() - uptime := time.Since(config.AppUptime).Round(time.Second) + uptime := time.Since(config.App.Started).Round(time.Second) system.Uptime = uptime.String() system.GoRoutines = runtime.NumGoroutine() diff --git a/handlers/core/link.go b/handlers/core/link.go index c6428f88..64b1a23c 100644 --- a/handlers/core/link.go +++ b/handlers/core/link.go @@ -10,13 +10,13 @@ import ( func GetLinkedSite(c *fiber.Ctx) error { switch c.Params("site") { case "discord": - return c.Redirect(config.AppLinkChatDiscord, fiber.StatusSeeOther) + return c.Redirect(config.App.Discord, fiber.StatusSeeOther) case "matrix": - return c.Redirect(config.AppLinkChatMatrix, fiber.StatusSeeOther) + return c.Redirect(config.App.Matrix, fiber.StatusSeeOther) case "opencollective": - return c.Redirect(config.AppLinkOpenCollective, fiber.StatusSeeOther) + return c.Redirect(config.App.OpenCollective, fiber.StatusSeeOther) case "source": - return c.Redirect(config.AppSourceCode, fiber.StatusSeeOther) + return c.Redirect(config.App.Repository, fiber.StatusSeeOther) default: u, _ := jwt.User(c) return c.Render("err", fiber.Map{ diff --git a/handlers/style/category.go b/handlers/style/category.go index e2a11dd0..ee726226 100644 --- a/handlers/style/category.go +++ b/handlers/style/category.go @@ -33,7 +33,7 @@ func GetCategory(c *fiber.Ctx) error { } c.Locals("Pagination", p) - cat, err := storage.GetStyleCategories(page, config.AppPageMaxItems) + cat, err := storage.GetStyleCategories(page, config.App.PageMaxItems) if err != nil { c.Locals("Title", "Failed to find categories") return c.Render("err", fiber.Map{}) diff --git a/handlers/style/explore.go b/handlers/style/explore.go index d61d407c..39d58757 100644 --- a/handlers/style/explore.go +++ b/handlers/style/explore.go @@ -39,7 +39,7 @@ func GetExplore(c *fiber.Ctx) error { c.Locals("Pagination", p) // Query for [sorted] styles. - s, err := storage.FindStyleCardsPaginated(p.Now, config.AppPageMaxItems, p.SortStyles()) + s, err := storage.FindStyleCardsPaginated(p.Now, config.App.PageMaxItems, p.SortStyles()) if err != nil { log.Database.Println("Failed to get styles:", err) c.Locals("Title", "Styles not found") diff --git a/handlers/user/profile.go b/handlers/user/profile.go index 4f51e6e1..1955e6aa 100644 --- a/handlers/user/profile.go +++ b/handlers/user/profile.go @@ -42,7 +42,7 @@ func Profile(c *fiber.Ctx) error { } c.Locals("Count", count) - size := config.AppPageMaxItems + size := config.App.PageMaxItems p := models.NewPagination(page, count, c.Query("sort"), c.Path()) if p.OutOfBounds() { return c.Redirect(p.URL(p.Now), 302) diff --git a/models/pagination.go b/models/pagination.go index d343b774..47ee388a 100644 --- a/models/pagination.go +++ b/models/pagination.go @@ -83,8 +83,8 @@ func (p *Pagination) calcItems(total int) { } // Calculate max page and remainder. - p.Max = total / config.AppPageMaxItems - if total%config.AppPageMaxItems > 0 { + p.Max = total / config.App.PageMaxItems + if total%config.App.PageMaxItems > 0 { p.Max++ } diff --git a/modules/config/config.go b/modules/config/config.go index 825fb936..09777a2b 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -17,6 +17,23 @@ type ( Production bool Addr string BaseURL string + + Name string + Description string + Codename string + Copyright string + Started time.Time + EmailRe string + PageMaxItems int + + Repository string + Discord string + Matrix string + OpenCollective string + + BuildCommit string + BuildCommitSHA string + BuildSignature string } config struct { @@ -30,10 +47,29 @@ var ( ) func defaultConfig() *config { + repo := "https://github.com/userstyles-world/userstyles.world" + started := time.Now() + return &config{ App: AppConfig{ - Addr: ":3000", - BaseURL: "http://localhost:3000", + Addr: ":3000", + BaseURL: "http://localhost:3000", + Name: "UserStyles.world", + Description: "A free and open-source, community-driven website for browsing and sharing UserCSS userstyles.", + Codename: "Fennec Fox", + Copyright: started.Format("2006"), + Started: started, + EmailRe: `^[a-zA-Z0-9.!#$%&’*+/=?^_\x60{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+$`, + PageMaxItems: 36, + + Repository: repo, + Discord: "https://discord.gg/P5zra4nFS2", + Matrix: "https://matrix.to/#/#userstyles:matrix.org", + OpenCollective: "https://opencollective.com/userstyles", + + BuildCommit: repo + "/commit/" + GitCommit, + BuildCommitSHA: fmt.Sprintf("%.8s", GitCommit), + BuildSignature: GitVersion, }, } } @@ -116,20 +152,6 @@ var ( LogFile = path.Join(DataDir, "userstyles.log") - AppName = "UserStyles.world" - AppCodeName = "Fennec Fox" - AppSourceCode = "https://github.com/userstyles-world/userstyles.world" - AppLatestCommit = AppSourceCode + "/commit/" + GitCommit - AppCommitSHA = fmt.Sprintf("%.7s", GitCommit) - AppUptime = time.Now() - AppPageMaxItems = 36 - - AppLinkChatDiscord = "https://discord.gg/P5zra4nFS2" - AppLinkChatMatrix = "https://matrix.to/#/#userstyles:matrix.org" - AppLinkOpenCollective = "https://opencollective.com/userstyles" - - AllowedEmailsRe = `^[a-zA-Z0-9.!#$%&’*+/=?^_\x60{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+$` - CachedCodeItems = getEnvInt("CACHED_CODE_ITEMS", 250) ProxyRealIP = getEnv("PROXY_REAL_IP", "") ) diff --git a/modules/database/init/utils.go b/modules/database/init/utils.go index 4a1d7554..484b1090 100644 --- a/modules/database/init/utils.go +++ b/modules/database/init/utils.go @@ -8,7 +8,7 @@ import ( ) func logLevel() logger.LogLevel { - switch config.DBDebug { + switch config.Database.Debug { case "error": return logger.Error case "warn": diff --git a/modules/storage/style_search.go b/modules/storage/style_search.go index d46442cf..984ed3d9 100644 --- a/modules/storage/style_search.go +++ b/modules/storage/style_search.go @@ -61,10 +61,10 @@ MATCH ?`) b.WriteString(sort) } b.WriteString(" LIMIT ") - b.WriteString(strconv.Itoa(config.AppPageMaxItems)) + b.WriteString(strconv.Itoa(config.App.PageMaxItems)) if page > 1 { b.WriteString(" OFFSET ") - b.WriteString(strconv.Itoa((page - 1) * config.AppPageMaxItems)) + b.WriteString(strconv.Itoa((page - 1) * config.App.PageMaxItems)) } var s []*StyleCard diff --git a/modules/templates/templates.go b/modules/templates/templates.go index f702f023..f0ab16b7 100644 --- a/modules/templates/templates.go +++ b/modules/templates/templates.go @@ -17,17 +17,6 @@ import ( "userstyles.world/modules/util" ) -var appConfig = map[string]string{ - "copyright": time.Now().Format("2006"), - "appName": config.AppName, - "appCodeName": config.AppCodeName, - "appVersion": config.GitVersion, - "appSourceCode": config.AppSourceCode, - "appLatestCommit": config.AppLatestCommit, - "appCommitSHA": config.AppCommitSHA, - "allowedEmailsRe": config.AllowedEmailsRe, -} - type sys struct { Uptime string GoRoutines int @@ -39,7 +28,7 @@ type sys struct { func status() sys { m := new(runtime.MemStats) runtime.ReadMemStats(m) - uptime := time.Since(config.AppUptime).Round(time.Second) + uptime := time.Since(config.App.Started).Round(time.Second) return sys{ Uptime: uptime.String(), @@ -53,10 +42,6 @@ func status() sys { func New(views http.FileSystem) *html.Engine { engine := html.NewFileSystem(views, ".tmpl") - engine.AddFunc("config", func(key string) string { - return appConfig[key] - }) - engine.AddFunc("sys", status) engine.AddFunc("comma", humanize.Comma) diff --git a/web/views/core/home.tmpl b/web/views/core/home.tmpl index a61a2ccb..49fa19c1 100644 --- a/web/views/core/home.tmpl +++ b/web/views/core/home.tmpl @@ -1,7 +1,7 @@ {{ if not .User.ID }}
    {{ template "partials/mascot" }}
    -

    {{ config "appName" }}

    +

    {{ .App.Name }}

    Free and open-source, community-driven platform for sharing and browsing UserCSS userstyles, and a replacement for UserStyles.org, made by the userstyles community.

    diff --git a/web/views/partials/footer.tmpl b/web/views/partials/footer.tmpl index a065e867..d7a54aac 100644 --- a/web/views/partials/footer.tmpl +++ b/web/views/partials/footer.tmpl @@ -2,15 +2,9 @@