- Move the embed.go at the root path of the project to be able to add
Models as embed/FS - Full restructural change to use a config Provider to avoid circular dependency - Refactor app.go to take those changes into account - Fix logging directory setup not creating when none
- commit
- a1ff1a2c69efb59704430872551e78cfa66cde91
- author
- bnema · · 2023-09-06 08:27 UTC
- parent
- 22ef2df
- signature
- unsigned
17 files · +154 -214
M .air.toml +1 -1
| @@ -7,7 +7,7 @@ tmp_dir = "tmp" | |||
| 7 | 7 | bin = "./tmp/main" | |
| 8 | 8 | cmd = "go build -o ./tmp/main cmd/main.go" | |
| 9 | 9 | delay = 0 | |
| 10 | - | exclude_dir = ["assets", "tmp", "vendor", "testdata"] | |
| 10 | + | exclude_dir = ["assets", "tmp", "vendor", "testdata", "node_modules"] | |
| 11 | 11 | exclude_file = [] | |
| 12 | 12 | exclude_regex = ["_test.go"] | |
| 13 | 13 | exclude_unchanged = false | |
| @@ -7,7 +7,7 @@ tmp_dir = "tmp" | |||||
| 7 | bin = "./tmp/main" | 7 | bin = "./tmp/main" | ||
| 8 | cmd = "go build -o ./tmp/main cmd/main.go" | 8 | cmd = "go build -o ./tmp/main cmd/main.go" | ||
| 9 | delay = 0 | 9 | delay = 0 | ||
| 10 | - | exclude_dir = ["assets", "tmp", "vendor", "testdata"] | 10 | + | exclude_dir = ["assets", "tmp", "vendor", "testdata", "node_modules"] |
| 11 | exclude_file = [] | 11 | exclude_file = [] | ||
| 12 | exclude_regex = ["_test.go"] | 12 | exclude_regex = ["_test.go"] | ||
| 13 | exclude_unchanged = false | 13 | exclude_unchanged = false | ||
M cmd/main.go +7 -8
| @@ -1,7 +1,6 @@ | |||
| 1 | 1 | package main | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | - | "embed" | |
| 5 | 4 | "os" | |
| 6 | 5 | ||
| 7 | 6 | "github.com/labstack/echo/v4" | |
| @@ -17,14 +16,14 @@ func main() { | |||
| 17 | 16 | if port == "" { | |
| 18 | 17 | port = "1323" | |
| 19 | 18 | } | |
| 20 | - | ||
| 21 | - | config := app.Config{ | |
| 22 | - | TemplateFS: root.TemplateFS.(embed.FS), | |
| 23 | - | PublicFS: root.PublicFS.(embed.FS), | |
| 24 | - | ModelFS: root.ModelFS, | |
| 25 | - | } | |
| 26 | 19 | // Initialize Gordon | |
| 27 | - | gordon, err := app.NewApp(&config) | |
| 20 | + | gordon, err := app.NewApp( | |
| 21 | + | &app.Config{ | |
| 22 | + | TemplateFS: root.TemplateFS, | |
| 23 | + | PublicFS: root.PublicFS, | |
| 24 | + | ModelFS: root.ModelFS, | |
| 25 | + | }, | |
| 26 | + | ) | |
| 28 | 27 | if err != nil { | |
| 29 | 28 | // Handle initialization error and exit | |
| 30 | 29 | gordon.AppLogger.Error().Err(err).Msg("Failed to initialize app") | |
| @@ -1,7 +1,6 @@ | |||||
| 1 | package main | 1 | package main | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | - | "embed" | |||
| 5 | "os" | 4 | "os" | ||
| 6 | 5 | ||||
| 7 | "github.com/labstack/echo/v4" | 6 | "github.com/labstack/echo/v4" | ||
| @@ -17,14 +16,14 @@ func main() { | |||||
| 17 | if port == "" { | 16 | if port == "" { | ||
| 18 | port = "1323" | 17 | port = "1323" | ||
| 19 | } | 18 | } | ||
| 20 | - | ||||
| 21 | - | config := app.Config{ | |||
| 22 | - | TemplateFS: root.TemplateFS.(embed.FS), | |||
| 23 | - | PublicFS: root.PublicFS.(embed.FS), | |||
| 24 | - | ModelFS: root.ModelFS, | |||
| 25 | - | } | |||
| 26 | // Initialize Gordon | 19 | // Initialize Gordon | ||
| 27 | - | gordon, err := app.NewApp(&config) | 20 | + | gordon, err := app.NewApp( |
| 21 | + | &app.Config{ | |||
| 22 | + | TemplateFS: root.TemplateFS, | |||
| 23 | + | PublicFS: root.PublicFS, | |||
| 24 | + | ModelFS: root.ModelFS, | |||
| 25 | + | }, | |||
| 26 | + | ) | |||
| 28 | if err != nil { | 27 | if err != nil { | ||
| 29 | // Handle initialization error and exit | 28 | // Handle initialization error and exit | ||
| 30 | gordon.AppLogger.Error().Err(err).Msg("Failed to initialize app") | 29 | gordon.AppLogger.Error().Err(err).Msg("Failed to initialize app") | ||
M config/config-provider.go +4 -4
| @@ -1,9 +1,9 @@ | |||
| 1 | 1 | package config | |
| 2 | 2 | ||
| 3 | - | import "embed" | |
| 3 | + | import "io/fs" | |
| 4 | 4 | ||
| 5 | 5 | type Provider interface { | |
| 6 | - | GetTemplateFS() embed.FS | |
| 7 | - | GetPublicFS() embed.FS | |
| 8 | - | GetModelFS() embed.FS | |
| 6 | + | GetTemplateFS() fs.FS | |
| 7 | + | GetPublicFS() fs.FS | |
| 8 | + | GetModelFS() fs.FS | |
| 9 | 9 | } | |
| @@ -1,9 +1,9 @@ | |||||
| 1 | package config | 1 | package config | ||
| 2 | 2 | ||||
| 3 | - | import "embed" | 3 | + | import "io/fs" |
| 4 | 4 | ||||
| 5 | type Provider interface { | 5 | type Provider interface { | ||
| 6 | - | GetTemplateFS() embed.FS | 6 | + | GetTemplateFS() fs.FS |
| 7 | - | GetPublicFS() embed.FS | 7 | + | GetPublicFS() fs.FS |
| 8 | - | GetModelFS() embed.FS | 8 | + | GetModelFS() fs.FS |
| 9 | } | 9 | } | ||
M embed.go +2 -6
| @@ -14,13 +14,9 @@ var public embed.FS | |||
| 14 | 14 | //go:embed internal/ui/templates/* | |
| 15 | 15 | var templates embed.FS | |
| 16 | 16 | ||
| 17 | - | // Embedding the YAML models | |
| 18 | - | // | |
| 19 | 17 | //go:embed pkg/templating/models/* | |
| 20 | - | var modelsEmbed embed.FS | |
| 18 | + | var models embed.FS | |
| 21 | 19 | ||
| 22 | 20 | var PublicFS = echo.MustSubFS(public, "internal/ui/public") | |
| 23 | 21 | var TemplateFS = echo.MustSubFS(templates, "internal/ui/templates") | |
| 24 | - | ||
| 25 | - | // Dont need Echo for models so we are just using embed.FS | |
| 26 | - | var ModelFS embed.FS = modelsEmbed | |
| 22 | + | var ModelFS = echo.MustSubFS(models, "pkg/templating/models") | |
| @@ -14,13 +14,9 @@ var public embed.FS | |||||
| 14 | //go:embed internal/ui/templates/* | 14 | //go:embed internal/ui/templates/* | ||
| 15 | var templates embed.FS | 15 | var templates embed.FS | ||
| 16 | 16 | ||||
| 17 | - | // Embedding the YAML models | |||
| 18 | - | // | |||
| 19 | //go:embed pkg/templating/models/* | 17 | //go:embed pkg/templating/models/* | ||
| 20 | - | var modelsEmbed embed.FS | 18 | + | var models embed.FS |
| 21 | 19 | ||||
| 22 | var PublicFS = echo.MustSubFS(public, "internal/ui/public") | 20 | var PublicFS = echo.MustSubFS(public, "internal/ui/public") | ||
| 23 | var TemplateFS = echo.MustSubFS(templates, "internal/ui/templates") | 21 | var TemplateFS = echo.MustSubFS(templates, "internal/ui/templates") | ||
| 24 | - | 22 | + | var ModelFS = echo.MustSubFS(models, "pkg/templating/models") | |
| 25 | - | // Dont need Echo for models so we are just using embed.FS | |||
| 26 | - | var ModelFS embed.FS = modelsEmbed | |||
M go.mod +1 -5
| @@ -6,6 +6,7 @@ require ( | |||
| 6 | 6 | github.com/PuerkitoBio/goquery v1.8.1 | |
| 7 | 7 | github.com/docker/docker v24.0.5+incompatible | |
| 8 | 8 | github.com/labstack/echo/v4 v4.11.1 | |
| 9 | + | github.com/microcosm-cc/bluemonday v1.0.25 | |
| 9 | 10 | github.com/tidwall/gjson v1.16.0 | |
| 10 | 11 | gopkg.in/yaml.v3 v3.0.1 | |
| 11 | 12 | ) | |
| @@ -25,10 +26,6 @@ require ( | |||
| 25 | 26 | github.com/gorilla/securecookie v1.1.1 // indirect | |
| 26 | 27 | github.com/gorilla/sessions v1.2.1 // indirect | |
| 27 | 28 | github.com/klauspost/compress v1.16.7 // indirect | |
| 28 | - | github.com/microcosm-cc/bluemonday v1.0.25 // indirect | |
| 29 | - | github.com/gorilla/securecookie v1.1.1 // indirect | |
| 30 | - | github.com/gorilla/sessions v1.2.1 // indirect | |
| 31 | - | github.com/klauspost/compress v1.16.7 // indirect | |
| 32 | 29 | github.com/moby/patternmatcher v0.6.0 // indirect | |
| 33 | 30 | github.com/moby/sys/sequential v0.5.0 // indirect | |
| 34 | 31 | github.com/moby/term v0.5.0 // indirect | |
| @@ -47,7 +44,6 @@ require ( | |||
| 47 | 44 | ) | |
| 48 | 45 | ||
| 49 | 46 | require ( | |
| 50 | - | github.com/google/safehtml v0.1.0 | |
| 51 | 47 | github.com/labstack/echo-contrib v0.15.0 | |
| 52 | 48 | github.com/labstack/gommon v0.4.0 | |
| 53 | 49 | github.com/mattn/go-colorable v0.1.13 // indirect | |
| @@ -6,6 +6,7 @@ require ( | |||||
| 6 | github.com/PuerkitoBio/goquery v1.8.1 | 6 | github.com/PuerkitoBio/goquery v1.8.1 | ||
| 7 | github.com/docker/docker v24.0.5+incompatible | 7 | github.com/docker/docker v24.0.5+incompatible | ||
| 8 | github.com/labstack/echo/v4 v4.11.1 | 8 | github.com/labstack/echo/v4 v4.11.1 | ||
| 9 | + | github.com/microcosm-cc/bluemonday v1.0.25 | |||
| 9 | github.com/tidwall/gjson v1.16.0 | 10 | github.com/tidwall/gjson v1.16.0 | ||
| 10 | gopkg.in/yaml.v3 v3.0.1 | 11 | gopkg.in/yaml.v3 v3.0.1 | ||
| 11 | ) | 12 | ) | ||
| @@ -25,10 +26,6 @@ require ( | |||||
| 25 | github.com/gorilla/securecookie v1.1.1 // indirect | 26 | github.com/gorilla/securecookie v1.1.1 // indirect | ||
| 26 | github.com/gorilla/sessions v1.2.1 // indirect | 27 | github.com/gorilla/sessions v1.2.1 // indirect | ||
| 27 | github.com/klauspost/compress v1.16.7 // indirect | 28 | github.com/klauspost/compress v1.16.7 // indirect | ||
| 28 | - | github.com/microcosm-cc/bluemonday v1.0.25 // indirect | |||
| 29 | - | github.com/gorilla/securecookie v1.1.1 // indirect | |||
| 30 | - | github.com/gorilla/sessions v1.2.1 // indirect | |||
| 31 | - | github.com/klauspost/compress v1.16.7 // indirect | |||
| 32 | github.com/moby/patternmatcher v0.6.0 // indirect | 29 | github.com/moby/patternmatcher v0.6.0 // indirect | ||
| 33 | github.com/moby/sys/sequential v0.5.0 // indirect | 30 | github.com/moby/sys/sequential v0.5.0 // indirect | ||
| 34 | github.com/moby/term v0.5.0 // indirect | 31 | github.com/moby/term v0.5.0 // indirect | ||
| @@ -47,7 +44,6 @@ require ( | |||||
| 47 | ) | 44 | ) | ||
| 48 | 45 | ||||
| 49 | require ( | 46 | require ( | ||
| 50 | - | github.com/google/safehtml v0.1.0 | |||
| 51 | github.com/labstack/echo-contrib v0.15.0 | 47 | github.com/labstack/echo-contrib v0.15.0 | ||
| 52 | github.com/labstack/gommon v0.4.0 | 48 | github.com/labstack/gommon v0.4.0 | ||
| 53 | github.com/mattn/go-colorable v0.1.13 // indirect | 49 | github.com/mattn/go-colorable v0.1.13 // indirect | ||
M go.sum +0 -4 generated file: collapsed
M internal/app/app.go +24 -23
| @@ -1,8 +1,9 @@ | |||
| 1 | 1 | package app | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | - | "embed" | |
| 4 | + | "fmt" | |
| 5 | 5 | "io" | |
| 6 | + | "io/fs" | |
| 6 | 7 | "os" | |
| 7 | 8 | "path/filepath" | |
| 8 | 9 | ||
| @@ -23,33 +24,33 @@ const ( | |||
| 23 | 24 | type App struct { | |
| 24 | 25 | AppLogger *utils.Logger | |
| 25 | 26 | HttpLogger *utils.Logger | |
| 27 | + | Config *Config | |
| 26 | 28 | } | |
| 27 | 29 | type Config struct { | |
| 28 | 30 | BuildDir string | |
| 29 | 31 | LogDir string | |
| 30 | - | TemplateFS embed.FS | |
| 31 | - | PublicFS embed.FS | |
| 32 | - | ModelFS embed.FS | |
| 32 | + | TemplateFS fs.FS | |
| 33 | + | PublicFS fs.FS | |
| 34 | + | ModelFS fs.FS | |
| 33 | 35 | } | |
| 34 | 36 | ||
| 35 | - | func (c *Config) GetTemplateFS() embed.FS { | |
| 37 | + | func (c *Config) GetTemplateFS() fs.FS { | |
| 36 | 38 | return c.TemplateFS | |
| 37 | 39 | } | |
| 38 | - | ||
| 39 | - | func (c *Config) GetPublicFS() embed.FS { | |
| 40 | + | func (c *Config) GetPublicFS() fs.FS { | |
| 40 | 41 | return c.PublicFS | |
| 41 | 42 | } | |
| 42 | 43 | ||
| 43 | - | func (c *Config) GetModelFS() embed.FS { | |
| 44 | + | func (c *Config) GetModelFS() fs.FS { | |
| 44 | 45 | return c.ModelFS | |
| 45 | 46 | } | |
| 46 | 47 | ||
| 47 | 48 | var _ config.Provider = &Config{} | |
| 48 | 49 | ||
| 49 | - | func loadConfig(templateFS, publicFS, modelFS embed.FS) Config { | |
| 50 | + | func defineEnv(config *Config) { | |
| 50 | 51 | // Get the environment variable | |
| 51 | 52 | env := os.Getenv(EnvVarName) | |
| 52 | - | // If the environment variable is empty, considere it is dev -> (/tmp) | |
| 53 | + | // If the environment variable is empty, consider it is dev -> (/tmp) | |
| 53 | 54 | buildDir := os.Getenv(BuildDirEnvVarName) | |
| 54 | 55 | if buildDir == "" { | |
| 55 | 56 | buildDir = DefaultBuildDir | |
| @@ -69,20 +70,17 @@ func loadConfig(templateFS, publicFS, modelFS embed.FS) Config { | |||
| 69 | 70 | buildDir = "./" + buildDir | |
| 70 | 71 | logDir = filepath.Join(buildDir, logDir) | |
| 71 | 72 | } | |
| 72 | - | ||
| 73 | - | return Config{ | |
| 74 | - | BuildDir: buildDir, | |
| 75 | - | LogDir: logDir, | |
| 76 | - | TemplateFS: templateFS, | |
| 77 | - | PublicFS: publicFS, | |
| 78 | - | ModelFS: modelFS, | |
| 79 | - | } | |
| 73 | + | config.BuildDir = buildDir | |
| 74 | + | config.LogDir = logDir | |
| 80 | 75 | } | |
| 76 | + | ||
| 81 | 77 | func NewApp(config *Config) (*App, error) { | |
| 78 | + | defineEnv(config) | |
| 82 | 79 | SetupLogging(config) | |
| 83 | 80 | app := &App{ | |
| 84 | 81 | AppLogger: utils.NewLogger().SetOutput(initializeLoggerOutput(config, "app.log")), | |
| 85 | 82 | HttpLogger: utils.NewLogger().SetOutput(initializeLoggerOutput(config, "http.log")), | |
| 83 | + | Config: config, | |
| 86 | 84 | } | |
| 87 | 85 | return app, nil | |
| 88 | 86 | } | |
| @@ -103,10 +101,13 @@ func initializeLoggerOutput(cfg *Config, logFile string) io.Writer { | |||
| 103 | 101 | consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout} | |
| 104 | 102 | return zerolog.MultiLevelWriter(file, consoleWriter) | |
| 105 | 103 | } | |
| 106 | - | ||
| 107 | - | func SetupLogging(cfg *Config) { | |
| 108 | - | err := utils.CreateLogsDir(cfg.LogDir) | |
| 109 | - | if err != nil { | |
| 110 | - | panic(err) | |
| 104 | + | func SetupLogging(config *Config) { | |
| 105 | + | // Check if the log directory exists | |
| 106 | + | if _, err := os.Stat(config.LogDir); os.IsNotExist(err) { | |
| 107 | + | // If not, create it | |
| 108 | + | err := os.MkdirAll(config.LogDir, 0755) | |
| 109 | + | if err != nil { | |
| 110 | + | panic(fmt.Sprintf("Failed to create log directory at path: %s, error: %s", config.LogDir, err)) | |
| 111 | + | } | |
| 111 | 112 | } | |
| 112 | 113 | } | |
| @@ -1,8 +1,9 @@ | |||||
| 1 | package app | 1 | package app | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | - | "embed" | 4 | + | "fmt" |
| 5 | "io" | 5 | "io" | ||
| 6 | + | "io/fs" | |||
| 6 | "os" | 7 | "os" | ||
| 7 | "path/filepath" | 8 | "path/filepath" | ||
| 8 | 9 | ||||
| @@ -23,33 +24,33 @@ const ( | |||||
| 23 | type App struct { | 24 | type App struct { | ||
| 24 | AppLogger *utils.Logger | 25 | AppLogger *utils.Logger | ||
| 25 | HttpLogger *utils.Logger | 26 | HttpLogger *utils.Logger | ||
| 27 | + | Config *Config | |||
| 26 | } | 28 | } | ||
| 27 | type Config struct { | 29 | type Config struct { | ||
| 28 | BuildDir string | 30 | BuildDir string | ||
| 29 | LogDir string | 31 | LogDir string | ||
| 30 | - | TemplateFS embed.FS | 32 | + | TemplateFS fs.FS |
| 31 | - | PublicFS embed.FS | 33 | + | PublicFS fs.FS |
| 32 | - | ModelFS embed.FS | 34 | + | ModelFS fs.FS |
| 33 | } | 35 | } | ||
| 34 | 36 | ||||
| 35 | - | func (c *Config) GetTemplateFS() embed.FS { | 37 | + | func (c *Config) GetTemplateFS() fs.FS { |
| 36 | return c.TemplateFS | 38 | return c.TemplateFS | ||
| 37 | } | 39 | } | ||
| 38 | - | 40 | + | func (c *Config) GetPublicFS() fs.FS { | |
| 39 | - | func (c *Config) GetPublicFS() embed.FS { | |||
| 40 | return c.PublicFS | 41 | return c.PublicFS | ||
| 41 | } | 42 | } | ||
| 42 | 43 | ||||
| 43 | - | func (c *Config) GetModelFS() embed.FS { | 44 | + | func (c *Config) GetModelFS() fs.FS { |
| 44 | return c.ModelFS | 45 | return c.ModelFS | ||
| 45 | } | 46 | } | ||
| 46 | 47 | ||||
| 47 | var _ config.Provider = &Config{} | 48 | var _ config.Provider = &Config{} | ||
| 48 | 49 | ||||
| 49 | - | func loadConfig(templateFS, publicFS, modelFS embed.FS) Config { | 50 | + | func defineEnv(config *Config) { |
| 50 | // Get the environment variable | 51 | // Get the environment variable | ||
| 51 | env := os.Getenv(EnvVarName) | 52 | env := os.Getenv(EnvVarName) | ||
| 52 | - | // If the environment variable is empty, considere it is dev -> (/tmp) | 53 | + | // If the environment variable is empty, consider it is dev -> (/tmp) |
| 53 | buildDir := os.Getenv(BuildDirEnvVarName) | 54 | buildDir := os.Getenv(BuildDirEnvVarName) | ||
| 54 | if buildDir == "" { | 55 | if buildDir == "" { | ||
| 55 | buildDir = DefaultBuildDir | 56 | buildDir = DefaultBuildDir | ||
| @@ -69,20 +70,17 @@ func loadConfig(templateFS, publicFS, modelFS embed.FS) Config { | |||||
| 69 | buildDir = "./" + buildDir | 70 | buildDir = "./" + buildDir | ||
| 70 | logDir = filepath.Join(buildDir, logDir) | 71 | logDir = filepath.Join(buildDir, logDir) | ||
| 71 | } | 72 | } | ||
| 72 | - | 73 | + | config.BuildDir = buildDir | |
| 73 | - | return Config{ | 74 | + | config.LogDir = logDir |
| 74 | - | BuildDir: buildDir, | |||
| 75 | - | LogDir: logDir, | |||
| 76 | - | TemplateFS: templateFS, | |||
| 77 | - | PublicFS: publicFS, | |||
| 78 | - | ModelFS: modelFS, | |||
| 79 | - | } | |||
| 80 | } | 75 | } | ||
| 76 | + | ||||
| 81 | func NewApp(config *Config) (*App, error) { | 77 | func NewApp(config *Config) (*App, error) { | ||
| 78 | + | defineEnv(config) | |||
| 82 | SetupLogging(config) | 79 | SetupLogging(config) | ||
| 83 | app := &App{ | 80 | app := &App{ | ||
| 84 | AppLogger: utils.NewLogger().SetOutput(initializeLoggerOutput(config, "app.log")), | 81 | AppLogger: utils.NewLogger().SetOutput(initializeLoggerOutput(config, "app.log")), | ||
| 85 | HttpLogger: utils.NewLogger().SetOutput(initializeLoggerOutput(config, "http.log")), | 82 | HttpLogger: utils.NewLogger().SetOutput(initializeLoggerOutput(config, "http.log")), | ||
| 83 | + | Config: config, | |||
| 86 | } | 84 | } | ||
| 87 | return app, nil | 85 | return app, nil | ||
| 88 | } | 86 | } | ||
| @@ -103,10 +101,13 @@ func initializeLoggerOutput(cfg *Config, logFile string) io.Writer { | |||||
| 103 | consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout} | 101 | consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout} | ||
| 104 | return zerolog.MultiLevelWriter(file, consoleWriter) | 102 | return zerolog.MultiLevelWriter(file, consoleWriter) | ||
| 105 | } | 103 | } | ||
| 106 | - | 104 | + | func SetupLogging(config *Config) { | |
| 107 | - | func SetupLogging(cfg *Config) { | 105 | + | // Check if the log directory exists |
| 108 | - | err := utils.CreateLogsDir(cfg.LogDir) | 106 | + | if _, err := os.Stat(config.LogDir); os.IsNotExist(err) { |
| 109 | - | if err != nil { | 107 | + | // If not, create it |
| 110 | - | panic(err) | 108 | + | err := os.MkdirAll(config.LogDir, 0755) |
| 109 | + | if err != nil { | |||
| 110 | + | panic(fmt.Sprintf("Failed to create log directory at path: %s, error: %s", config.LogDir, err)) | |||
| 111 | + | } | |||
| 111 | } | 112 | } | ||
| 112 | } | 113 | } | ||
M internal/http/handlers/errors.go +11 -41
| @@ -5,50 +5,20 @@ import ( | |||
| 5 | 5 | "net/http" | |
| 6 | 6 | ||
| 7 | 7 | "github.com/labstack/echo/v4" | |
| 8 | - | "gogs.bnema.dev/gordon-echo/internal/app" | |
| 9 | - | "gogs.bnema.dev/gordon-echo/pkg/utils" | |
| 10 | 8 | ) | |
| 11 | 9 | ||
| 12 | - | type AppConfig struct { | |
| 13 | - | *app.Config | |
| 14 | - | } | |
| 15 | - | ||
| 16 | - | func (a *AppConfig) Error404Handler(err error, c echo.Context) { | |
| 10 | + | func ErrorNumberHandler(err error, c echo.Context) { | |
| 11 | + | code := http.StatusInternalServerError | |
| 17 | 12 | if he, ok := err.(*echo.HTTPError); ok { | |
| 18 | - | if he.Code == http.StatusNotFound { | |
| 19 | - | renderer, err := utils.GetRenderer("404.gohtml", a.Config.PublicFS, utils.NewLogger()) | |
| 20 | - | if err != nil { | |
| 21 | - | c.String(http.StatusInternalServerError, fmt.Sprintf("%v", err)) | |
| 22 | - | } | |
| 23 | - | ||
| 24 | - | data := map[string]interface{}{ | |
| 25 | - | "website": map[string]interface{}{ | |
| 26 | - | "title": "Page Not Found", | |
| 27 | - | }, | |
| 28 | - | } | |
| 29 | - | ||
| 30 | - | html, err := renderer.Render(data) | |
| 31 | - | if err != nil { | |
| 32 | - | c.String(http.StatusInternalServerError, fmt.Sprintf("%v", err)) | |
| 33 | - | } | |
| 34 | - | ||
| 35 | - | c.HTML(http.StatusNotFound, html) | |
| 36 | - | return | |
| 37 | - | } | |
| 13 | + | code = he.Code | |
| 38 | 14 | } | |
| 39 | 15 | ||
| 40 | - | } | |
| 41 | - | ||
| 42 | - | func Error200Handler(err error, c echo.Context) { | |
| 43 | - | // Just print the error for now | |
| 44 | - | fmt.Println(err) | |
| 45 | - | } | |
| 46 | - | ||
| 47 | - | func Error403Handler(err error, c echo.Context) { | |
| 48 | - | // Just print the error for now | |
| 49 | - | fmt.Println(err) | |
| 50 | - | } | |
| 51 | - | ||
| 52 | - | func ErrorNumberHandler(err error, c echo.Context) { | |
| 53 | - | // For Each Error Code, we can have a different handler | |
| 16 | + | switch code { | |
| 17 | + | case http.StatusNotFound: | |
| 18 | + | c.String(code, fmt.Sprintf("An error occurred: %v", err)) | |
| 19 | + | case http.StatusForbidden: | |
| 20 | + | c.String(code, fmt.Sprintf("An error occurred: %v", err)) | |
| 21 | + | default: | |
| 22 | + | c.String(code, fmt.Sprintf("An error occurred: %v", err)) | |
| 23 | + | } | |
| 54 | 24 | } | |
| @@ -5,50 +5,20 @@ import ( | |||||
| 5 | "net/http" | 5 | "net/http" | ||
| 6 | 6 | ||||
| 7 | "github.com/labstack/echo/v4" | 7 | "github.com/labstack/echo/v4" | ||
| 8 | - | "gogs.bnema.dev/gordon-echo/internal/app" | |||
| 9 | - | "gogs.bnema.dev/gordon-echo/pkg/utils" | |||
| 10 | ) | 8 | ) | ||
| 11 | 9 | ||||
| 12 | - | type AppConfig struct { | 10 | + | func ErrorNumberHandler(err error, c echo.Context) { |
| 13 | - | *app.Config | 11 | + | code := http.StatusInternalServerError |
| 14 | - | } | |||
| 15 | - | ||||
| 16 | - | func (a *AppConfig) Error404Handler(err error, c echo.Context) { | |||
| 17 | if he, ok := err.(*echo.HTTPError); ok { | 12 | if he, ok := err.(*echo.HTTPError); ok { | ||
| 18 | - | if he.Code == http.StatusNotFound { | 13 | + | code = he.Code |
| 19 | - | renderer, err := utils.GetRenderer("404.gohtml", a.Config.PublicFS, utils.NewLogger()) | |||
| 20 | - | if err != nil { | |||
| 21 | - | c.String(http.StatusInternalServerError, fmt.Sprintf("%v", err)) | |||
| 22 | - | } | |||
| 23 | - | ||||
| 24 | - | data := map[string]interface{}{ | |||
| 25 | - | "website": map[string]interface{}{ | |||
| 26 | - | "title": "Page Not Found", | |||
| 27 | - | }, | |||
| 28 | - | } | |||
| 29 | - | ||||
| 30 | - | html, err := renderer.Render(data) | |||
| 31 | - | if err != nil { | |||
| 32 | - | c.String(http.StatusInternalServerError, fmt.Sprintf("%v", err)) | |||
| 33 | - | } | |||
| 34 | - | ||||
| 35 | - | c.HTML(http.StatusNotFound, html) | |||
| 36 | - | return | |||
| 37 | - | } | |||
| 38 | } | 14 | } | ||
| 39 | 15 | ||||
| 40 | - | } | 16 | + | switch code { |
| 41 | - | 17 | + | case http.StatusNotFound: | |
| 42 | - | func Error200Handler(err error, c echo.Context) { | 18 | + | c.String(code, fmt.Sprintf("An error occurred: %v", err)) |
| 43 | - | // Just print the error for now | 19 | + | case http.StatusForbidden: |
| 44 | - | fmt.Println(err) | 20 | + | c.String(code, fmt.Sprintf("An error occurred: %v", err)) |
| 45 | - | } | 21 | + | default: |
| 46 | - | 22 | + | c.String(code, fmt.Sprintf("An error occurred: %v", err)) | |
| 47 | - | func Error403Handler(err error, c echo.Context) { | 23 | + | } |
| 48 | - | // Just print the error for now | |||
| 49 | - | fmt.Println(err) | |||
| 50 | - | } | |||
| 51 | - | ||||
| 52 | - | func ErrorNumberHandler(err error, c echo.Context) { | |||
| 53 | - | // For Each Error Code, we can have a different handler | |||
| 54 | } | 24 | } | ||
M internal/http/handlers/htmx-fragment-header.go +14 -6
| @@ -4,6 +4,7 @@ import ( | |||
| 4 | 4 | "net/http" | |
| 5 | 5 | ||
| 6 | 6 | "github.com/labstack/echo/v4" | |
| 7 | + | "gogs.bnema.dev/gordon-echo/config" | |
| 7 | 8 | "gogs.bnema.dev/gordon-echo/internal/http/middlewares" | |
| 8 | 9 | "gogs.bnema.dev/gordon-echo/pkg/htmx" | |
| 9 | 10 | "gogs.bnema.dev/gordon-echo/pkg/utils" | |
| @@ -12,9 +13,9 @@ import ( | |||
| 12 | 13 | func HTMXHandler(c echo.Context) error { | |
| 13 | 14 | logger, ok := c.Get("logger").(*utils.Logger) | |
| 14 | 15 | if !ok { | |
| 15 | - | logger.Error().Msg("Failed to get logger from context") | |
| 16 | 16 | return c.String(http.StatusInternalServerError, "Failed to get logger from context") | |
| 17 | 17 | } | |
| 18 | + | ||
| 18 | 19 | // Detect and extract HTMX data from the request | |
| 19 | 20 | htmxRequest, err := htmx.GetRequest(c) | |
| 20 | 21 | if err != nil { | |
| @@ -30,14 +31,19 @@ func HTMXHandler(c echo.Context) error { | |||
| 30 | 31 | ||
| 31 | 32 | // If it's a GET request, handle the fragment | |
| 32 | 33 | if c.Request().Method == http.MethodGet { | |
| 33 | - | return htmxFragmentHandler(c, logger) | |
| 34 | + | cfg, ok := c.Get("config").(config.Provider) | |
| 35 | + | if !ok { | |
| 36 | + | logger.Error().Msg("Failed to get config provider from context") | |
| 37 | + | return c.String(http.StatusInternalServerError, "Failed to get config provider from context") | |
| 38 | + | } | |
| 39 | + | return htmxFragmentHandler(c, logger, cfg) | |
| 34 | 40 | } | |
| 35 | 41 | ||
| 36 | 42 | // For other HTTP methods, return an error | |
| 37 | 43 | return c.String(http.StatusBadRequest, "Invalid request method") | |
| 38 | 44 | } | |
| 39 | 45 | ||
| 40 | - | func htmxFragmentHandler(c echo.Context, logger *utils.Logger) error { | |
| 46 | + | func htmxFragmentHandler(c echo.Context, logger *utils.Logger, fs config.Provider) error { | |
| 41 | 47 | fragment := c.Request().Header.Get("HX-Fragment") | |
| 42 | 48 | if fragment == "" { | |
| 43 | 49 | logger.Error().Msg("Missing fragment header") | |
| @@ -45,16 +51,18 @@ func htmxFragmentHandler(c echo.Context, logger *utils.Logger) error { | |||
| 45 | 51 | } | |
| 46 | 52 | ||
| 47 | 53 | lang := c.Get(middlewares.LangKey).(string) | |
| 48 | - | staticData, err := utils.PopulateDataFromYAML(lang) | |
| 54 | + | var data utils.StringsYamlData | |
| 55 | + | err := utils.PopulateDataFromYAML(lang, fs.GetTemplateFS(), "strings.yml", &data) | |
| 49 | 56 | if err != nil { | |
| 50 | 57 | logger.Error().Err(err).Msg("Failed to populate data from YAML") | |
| 51 | 58 | return err | |
| 52 | 59 | } | |
| 53 | 60 | ||
| 54 | - | content, err := utils.GetHTMLFragmentByID(fragment, staticData.CurrentLang) | |
| 55 | - | if err != nil { | |
| 61 | + | content, err := utils.GetHTMLFragmentByID(fragment, data.CurrentLang, fs) | |
| 62 | + | if err != nil { // <-- This check was missing | |
| 56 | 63 | logger.Error().Err(err).Msg("Failed to get fragment") | |
| 57 | 64 | return c.String(http.StatusInternalServerError, err.Error()) | |
| 58 | 65 | } | |
| 66 | + | ||
| 59 | 67 | return c.HTML(http.StatusOK, content) | |
| 60 | 68 | } | |
| @@ -4,6 +4,7 @@ import ( | |||||
| 4 | "net/http" | 4 | "net/http" | ||
| 5 | 5 | ||||
| 6 | "github.com/labstack/echo/v4" | 6 | "github.com/labstack/echo/v4" | ||
| 7 | + | "gogs.bnema.dev/gordon-echo/config" | |||
| 7 | "gogs.bnema.dev/gordon-echo/internal/http/middlewares" | 8 | "gogs.bnema.dev/gordon-echo/internal/http/middlewares" | ||
| 8 | "gogs.bnema.dev/gordon-echo/pkg/htmx" | 9 | "gogs.bnema.dev/gordon-echo/pkg/htmx" | ||
| 9 | "gogs.bnema.dev/gordon-echo/pkg/utils" | 10 | "gogs.bnema.dev/gordon-echo/pkg/utils" | ||
| @@ -12,9 +13,9 @@ import ( | |||||
| 12 | func HTMXHandler(c echo.Context) error { | 13 | func HTMXHandler(c echo.Context) error { | ||
| 13 | logger, ok := c.Get("logger").(*utils.Logger) | 14 | logger, ok := c.Get("logger").(*utils.Logger) | ||
| 14 | if !ok { | 15 | if !ok { | ||
| 15 | - | logger.Error().Msg("Failed to get logger from context") | |||
| 16 | return c.String(http.StatusInternalServerError, "Failed to get logger from context") | 16 | return c.String(http.StatusInternalServerError, "Failed to get logger from context") | ||
| 17 | } | 17 | } | ||
| 18 | + | ||||
| 18 | // Detect and extract HTMX data from the request | 19 | // Detect and extract HTMX data from the request | ||
| 19 | htmxRequest, err := htmx.GetRequest(c) | 20 | htmxRequest, err := htmx.GetRequest(c) | ||
| 20 | if err != nil { | 21 | if err != nil { | ||
| @@ -30,14 +31,19 @@ func HTMXHandler(c echo.Context) error { | |||||
| 30 | 31 | ||||
| 31 | // If it's a GET request, handle the fragment | 32 | // If it's a GET request, handle the fragment | ||
| 32 | if c.Request().Method == http.MethodGet { | 33 | if c.Request().Method == http.MethodGet { | ||
| 33 | - | return htmxFragmentHandler(c, logger) | 34 | + | cfg, ok := c.Get("config").(config.Provider) |
| 35 | + | if !ok { | |||
| 36 | + | logger.Error().Msg("Failed to get config provider from context") | |||
| 37 | + | return c.String(http.StatusInternalServerError, "Failed to get config provider from context") | |||
| 38 | + | } | |||
| 39 | + | return htmxFragmentHandler(c, logger, cfg) | |||
| 34 | } | 40 | } | ||
| 35 | 41 | ||||
| 36 | // For other HTTP methods, return an error | 42 | // For other HTTP methods, return an error | ||
| 37 | return c.String(http.StatusBadRequest, "Invalid request method") | 43 | return c.String(http.StatusBadRequest, "Invalid request method") | ||
| 38 | } | 44 | } | ||
| 39 | 45 | ||||
| 40 | - | func htmxFragmentHandler(c echo.Context, logger *utils.Logger) error { | 46 | + | func htmxFragmentHandler(c echo.Context, logger *utils.Logger, fs config.Provider) error { |
| 41 | fragment := c.Request().Header.Get("HX-Fragment") | 47 | fragment := c.Request().Header.Get("HX-Fragment") | ||
| 42 | if fragment == "" { | 48 | if fragment == "" { | ||
| 43 | logger.Error().Msg("Missing fragment header") | 49 | logger.Error().Msg("Missing fragment header") | ||
| @@ -45,16 +51,18 @@ func htmxFragmentHandler(c echo.Context, logger *utils.Logger) error { | |||||
| 45 | } | 51 | } | ||
| 46 | 52 | ||||
| 47 | lang := c.Get(middlewares.LangKey).(string) | 53 | lang := c.Get(middlewares.LangKey).(string) | ||
| 48 | - | staticData, err := utils.PopulateDataFromYAML(lang) | 54 | + | var data utils.StringsYamlData |
| 55 | + | err := utils.PopulateDataFromYAML(lang, fs.GetTemplateFS(), "strings.yml", &data) | |||
| 49 | if err != nil { | 56 | if err != nil { | ||
| 50 | logger.Error().Err(err).Msg("Failed to populate data from YAML") | 57 | logger.Error().Err(err).Msg("Failed to populate data from YAML") | ||
| 51 | return err | 58 | return err | ||
| 52 | } | 59 | } | ||
| 53 | 60 | ||||
| 54 | - | content, err := utils.GetHTMLFragmentByID(fragment, staticData.CurrentLang) | 61 | + | content, err := utils.GetHTMLFragmentByID(fragment, data.CurrentLang, fs) |
| 55 | - | if err != nil { | 62 | + | if err != nil { // <-- This check was missing |
| 56 | logger.Error().Err(err).Msg("Failed to get fragment") | 63 | logger.Error().Err(err).Msg("Failed to get fragment") | ||
| 57 | return c.String(http.StatusInternalServerError, err.Error()) | 64 | return c.String(http.StatusInternalServerError, err.Error()) | ||
| 58 | } | 65 | } | ||
| 66 | + | ||||
| 59 | return c.HTML(http.StatusOK, content) | 67 | return c.HTML(http.StatusOK, content) | ||
| 60 | } | 68 | } | ||
M internal/http/routes/admin-routes.go +13 -13
| @@ -10,11 +10,7 @@ import ( | |||
| 10 | 10 | "gogs.bnema.dev/gordon-echo/pkg/utils" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | - | type AppConfig struct { | |
| 14 | - | Config config.Provider | |
| 15 | - | } | |
| 16 | - | ||
| 17 | - | func (a *AppConfig) AdminRoute(c echo.Context) error { | |
| 13 | + | func AdminRoute(c echo.Context, provider config.Provider) error { | |
| 18 | 14 | // Check if it's the first launch of the app | |
| 19 | 15 | if utils.IsConfigFilePresent() { | |
| 20 | 16 | fmt.Println("Fresh installation detected, redirecting to /admin/install") | |
| @@ -24,17 +20,19 @@ func (a *AppConfig) AdminRoute(c echo.Context) error { | |||
| 24 | 20 | // Retrieve the current language from the context | |
| 25 | 21 | lang := c.Get(middlewares.LangKey).(string) | |
| 26 | 22 | ||
| 27 | - | staticData, err := a.PopulateDataFromYAML(lang) | |
| 23 | + | // Assuming the filename is "strings.yml" | |
| 24 | + | var data utils.StringsYamlData | |
| 25 | + | err := utils.PopulateDataFromYAML(lang, provider.GetTemplateFS(), "strings.yml", &data) | |
| 28 | 26 | if err != nil { | |
| 29 | 27 | return err | |
| 30 | 28 | } | |
| 31 | 29 | ||
| 32 | - | renderer, err := utils.GetRenderer("index.gohtml", a.Config.GetPublicFS(), utils.NewLogger()) | |
| 30 | + | renderer, err := utils.GetRenderer("index.gohtml", provider.GetPublicFS(), utils.NewLogger()) | |
| 33 | 31 | if err != nil { | |
| 34 | 32 | return err | |
| 35 | 33 | } | |
| 36 | 34 | ||
| 37 | - | html, err := renderer.Render(staticData.CurrentLang) | |
| 35 | + | html, err := renderer.Render(data.CurrentLang) | |
| 38 | 36 | if err != nil { | |
| 39 | 37 | // Return the error into NewLogger() to log it | |
| 40 | 38 | utils.NewLogger().Error() | |
| @@ -44,7 +42,7 @@ func (a *AppConfig) AdminRoute(c echo.Context) error { | |||
| 44 | 42 | return c.HTML(http.StatusOK, html) | |
| 45 | 43 | } | |
| 46 | 44 | ||
| 47 | - | func (a *AppConfig) InstallRoute(c echo.Context) error { | |
| 45 | + | func InstallRoute(c echo.Context, provider config.Provider) error { | |
| 48 | 46 | // Check if it's the first launch of the app | |
| 49 | 47 | if !utils.IsConfigFilePresent() { | |
| 50 | 48 | fmt.Println("Config file already present, redirecting to /admin") | |
| @@ -54,17 +52,19 @@ func (a *AppConfig) InstallRoute(c echo.Context) error { | |||
| 54 | 52 | // Retrieve the current language from the context | |
| 55 | 53 | lang := c.Get(middlewares.LangKey).(string) | |
| 56 | 54 | ||
| 57 | - | staticData, err := a.PopulateDataFromYAML(lang) | |
| 55 | + | var data utils.StringsYamlData | |
| 56 | + | err := utils.PopulateDataFromYAML(lang, provider.GetTemplateFS(), "strings.yml", &data) | |
| 58 | 57 | if err != nil { | |
| 59 | - | return err | |
| 58 | + | return fmt.Errorf("failed to populate data from YAML: %w", err) | |
| 60 | 59 | } | |
| 61 | 60 | ||
| 62 | - | renderer, err := utils.GetRenderer("install.gohtml", a.Config.GetTemplateFS(), utils.NewLogger()) | |
| 61 | + | renderer, err := utils.GetRenderer("install.gohtml", provider.GetTemplateFS(), utils.NewLogger()) | |
| 62 | + | fmt.Println(renderer) | |
| 63 | 63 | if err != nil { | |
| 64 | 64 | return err | |
| 65 | 65 | } | |
| 66 | 66 | ||
| 67 | - | html, err := renderer.Render(staticData.CurrentLang) | |
| 67 | + | html, err := renderer.Render(data.CurrentLang) | |
| 68 | 68 | if err != nil { | |
| 69 | 69 | // Return the error into NewLogger() to log it | |
| 70 | 70 | utils.NewLogger().Error() | |
| @@ -10,11 +10,7 @@ import ( | |||||
| 10 | "gogs.bnema.dev/gordon-echo/pkg/utils" | 10 | "gogs.bnema.dev/gordon-echo/pkg/utils" | ||
| 11 | ) | 11 | ) | ||
| 12 | 12 | ||||
| 13 | - | type AppConfig struct { | 13 | + | func AdminRoute(c echo.Context, provider config.Provider) error { |
| 14 | - | Config config.Provider | |||
| 15 | - | } | |||
| 16 | - | ||||
| 17 | - | func (a *AppConfig) AdminRoute(c echo.Context) error { | |||
| 18 | // Check if it's the first launch of the app | 14 | // Check if it's the first launch of the app | ||
| 19 | if utils.IsConfigFilePresent() { | 15 | if utils.IsConfigFilePresent() { | ||
| 20 | fmt.Println("Fresh installation detected, redirecting to /admin/install") | 16 | fmt.Println("Fresh installation detected, redirecting to /admin/install") | ||
| @@ -24,17 +20,19 @@ func (a *AppConfig) AdminRoute(c echo.Context) error { | |||||
| 24 | // Retrieve the current language from the context | 20 | // Retrieve the current language from the context | ||
| 25 | lang := c.Get(middlewares.LangKey).(string) | 21 | lang := c.Get(middlewares.LangKey).(string) | ||
| 26 | 22 | ||||
| 27 | - | staticData, err := a.PopulateDataFromYAML(lang) | 23 | + | // Assuming the filename is "strings.yml" |
| 24 | + | var data utils.StringsYamlData | |||
| 25 | + | err := utils.PopulateDataFromYAML(lang, provider.GetTemplateFS(), "strings.yml", &data) | |||
| 28 | if err != nil { | 26 | if err != nil { | ||
| 29 | return err | 27 | return err | ||
| 30 | } | 28 | } | ||
| 31 | 29 | ||||
| 32 | - | renderer, err := utils.GetRenderer("index.gohtml", a.Config.GetPublicFS(), utils.NewLogger()) | 30 | + | renderer, err := utils.GetRenderer("index.gohtml", provider.GetPublicFS(), utils.NewLogger()) |
| 33 | if err != nil { | 31 | if err != nil { | ||
| 34 | return err | 32 | return err | ||
| 35 | } | 33 | } | ||
| 36 | 34 | ||||
| 37 | - | html, err := renderer.Render(staticData.CurrentLang) | 35 | + | html, err := renderer.Render(data.CurrentLang) |
| 38 | if err != nil { | 36 | if err != nil { | ||
| 39 | // Return the error into NewLogger() to log it | 37 | // Return the error into NewLogger() to log it | ||
| 40 | utils.NewLogger().Error() | 38 | utils.NewLogger().Error() | ||
| @@ -44,7 +42,7 @@ func (a *AppConfig) AdminRoute(c echo.Context) error { | |||||
| 44 | return c.HTML(http.StatusOK, html) | 42 | return c.HTML(http.StatusOK, html) | ||
| 45 | } | 43 | } | ||
| 46 | 44 | ||||
| 47 | - | func (a *AppConfig) InstallRoute(c echo.Context) error { | 45 | + | func InstallRoute(c echo.Context, provider config.Provider) error { |
| 48 | // Check if it's the first launch of the app | 46 | // Check if it's the first launch of the app | ||
| 49 | if !utils.IsConfigFilePresent() { | 47 | if !utils.IsConfigFilePresent() { | ||
| 50 | fmt.Println("Config file already present, redirecting to /admin") | 48 | fmt.Println("Config file already present, redirecting to /admin") | ||
| @@ -54,17 +52,19 @@ func (a *AppConfig) InstallRoute(c echo.Context) error { | |||||
| 54 | // Retrieve the current language from the context | 52 | // Retrieve the current language from the context | ||
| 55 | lang := c.Get(middlewares.LangKey).(string) | 53 | lang := c.Get(middlewares.LangKey).(string) | ||
| 56 | 54 | ||||
| 57 | - | staticData, err := a.PopulateDataFromYAML(lang) | 55 | + | var data utils.StringsYamlData |
| 56 | + | err := utils.PopulateDataFromYAML(lang, provider.GetTemplateFS(), "strings.yml", &data) | |||
| 58 | if err != nil { | 57 | if err != nil { | ||
| 59 | - | return err | 58 | + | return fmt.Errorf("failed to populate data from YAML: %w", err) |
| 60 | } | 59 | } | ||
| 61 | 60 | ||||
| 62 | - | renderer, err := utils.GetRenderer("install.gohtml", a.Config.GetTemplateFS(), utils.NewLogger()) | 61 | + | renderer, err := utils.GetRenderer("install.gohtml", provider.GetTemplateFS(), utils.NewLogger()) |
| 62 | + | fmt.Println(renderer) | |||
| 63 | if err != nil { | 63 | if err != nil { | ||
| 64 | return err | 64 | return err | ||
| 65 | } | 65 | } | ||
| 66 | 66 | ||||
| 67 | - | html, err := renderer.Render(staticData.CurrentLang) | 67 | + | html, err := renderer.Render(data.CurrentLang) |
| 68 | if err != nil { | 68 | if err != nil { | ||
| 69 | // Return the error into NewLogger() to log it | 69 | // Return the error into NewLogger() to log it | ||
| 70 | utils.NewLogger().Error() | 70 | utils.NewLogger().Error() | ||
M internal/http/routes/router.go +19 -9
| @@ -2,6 +2,7 @@ package routes | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "github.com/labstack/echo/v4" | |
| 5 | + | "gogs.bnema.dev/gordon-echo/config" | |
| 5 | 6 | "gogs.bnema.dev/gordon-echo/internal/app" | |
| 6 | 7 | "gogs.bnema.dev/gordon-echo/internal/http/handlers" | |
| 7 | 8 | "gogs.bnema.dev/gordon-echo/internal/http/middlewares" | |
| @@ -17,20 +18,26 @@ func ConfigureRouter(e *echo.Echo, app *app.App) *echo.Echo { | |||
| 17 | 18 | //e.Use(middlewares.SecureMiddleware()) // Need testing | |
| 18 | 19 | ||
| 19 | 20 | // Register routes | |
| 20 | - | e = bindAdminRoutes(e) | |
| 21 | - | e = bindHtmxRoutes(e) | |
| 22 | - | e = bindStaticRoutes(e) | |
| 21 | + | e = bindAdminRoutes(e, app.Config) | |
| 22 | + | e = bindHtmxRoutes(e, app.Config) | |
| 23 | + | e = bindStaticRoutes(e, app.Config) | |
| 23 | 24 | e.HTTPErrorHandler = handlers.ErrorNumberHandler | |
| 24 | 25 | return e | |
| 25 | 26 | } | |
| 26 | - | func bindAdminRoutes(e *echo.Echo) *echo.Echo { | |
| 27 | - | e.GET("/admin", AdminRoute) | |
| 28 | - | e.GET("/admin/install", InstallRoute) | |
| 27 | + | func bindAdminRoutes(e *echo.Echo, config config.Provider) *echo.Echo { | |
| 28 | + | // We pass the embed config to the routes so we can parse the strings.yml file | |
| 29 | + | e.GET("/admin", func(c echo.Context) error { | |
| 30 | + | return AdminRoute(c, config) | |
| 31 | + | }) | |
| 32 | + | e.GET("/admin/install", func(c echo.Context) error { | |
| 33 | + | return InstallRoute(c, config) | |
| 34 | + | }) | |
| 35 | + | ||
| 29 | 36 | e.POST("/admin/install/traefik", handlers.TraefikInstallerHandler) | |
| 30 | 37 | return e | |
| 31 | 38 | } | |
| 32 | 39 | ||
| 33 | - | func bindHtmxRoutes(e *echo.Echo) *echo.Echo { | |
| 40 | + | func bindHtmxRoutes(e *echo.Echo, config config.Provider) *echo.Echo { | |
| 34 | 41 | e.GET("/htmx", handlers.HTMXHandler) | |
| 35 | 42 | e.GET("/htmx/fragment", handlers.HTMXHandler) | |
| 36 | 43 | e.POST("/htmx", handlers.HTMXHandler) | |
| @@ -38,7 +45,10 @@ func bindHtmxRoutes(e *echo.Echo) *echo.Echo { | |||
| 38 | 45 | return e | |
| 39 | 46 | } | |
| 40 | 47 | ||
| 41 | - | func bindStaticRoutes(e *echo.Echo) *echo.Echo { | |
| 42 | - | e.GET("/*", StaticRoute) | |
| 48 | + | func bindStaticRoutes(e *echo.Echo, config config.Provider) *echo.Echo { | |
| 49 | + | ||
| 50 | + | e.GET("/*", func(c echo.Context) error { | |
| 51 | + | return StaticRoute(c, config) | |
| 52 | + | }) | |
| 43 | 53 | return e | |
| 44 | 54 | } | |
| @@ -2,6 +2,7 @@ package routes | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "github.com/labstack/echo/v4" | 4 | "github.com/labstack/echo/v4" | ||
| 5 | + | "gogs.bnema.dev/gordon-echo/config" | |||
| 5 | "gogs.bnema.dev/gordon-echo/internal/app" | 6 | "gogs.bnema.dev/gordon-echo/internal/app" | ||
| 6 | "gogs.bnema.dev/gordon-echo/internal/http/handlers" | 7 | "gogs.bnema.dev/gordon-echo/internal/http/handlers" | ||
| 7 | "gogs.bnema.dev/gordon-echo/internal/http/middlewares" | 8 | "gogs.bnema.dev/gordon-echo/internal/http/middlewares" | ||
| @@ -17,20 +18,26 @@ func ConfigureRouter(e *echo.Echo, app *app.App) *echo.Echo { | |||||
| 17 | //e.Use(middlewares.SecureMiddleware()) // Need testing | 18 | //e.Use(middlewares.SecureMiddleware()) // Need testing | ||
| 18 | 19 | ||||
| 19 | // Register routes | 20 | // Register routes | ||
| 20 | - | e = bindAdminRoutes(e) | 21 | + | e = bindAdminRoutes(e, app.Config) |
| 21 | - | e = bindHtmxRoutes(e) | 22 | + | e = bindHtmxRoutes(e, app.Config) |
| 22 | - | e = bindStaticRoutes(e) | 23 | + | e = bindStaticRoutes(e, app.Config) |
| 23 | e.HTTPErrorHandler = handlers.ErrorNumberHandler | 24 | e.HTTPErrorHandler = handlers.ErrorNumberHandler | ||
| 24 | return e | 25 | return e | ||
| 25 | } | 26 | } | ||
| 26 | - | func bindAdminRoutes(e *echo.Echo) *echo.Echo { | 27 | + | func bindAdminRoutes(e *echo.Echo, config config.Provider) *echo.Echo { |
| 27 | - | e.GET("/admin", AdminRoute) | 28 | + | // We pass the embed config to the routes so we can parse the strings.yml file |
| 28 | - | e.GET("/admin/install", InstallRoute) | 29 | + | e.GET("/admin", func(c echo.Context) error { |
| 30 | + | return AdminRoute(c, config) | |||
| 31 | + | }) | |||
| 32 | + | e.GET("/admin/install", func(c echo.Context) error { | |||
| 33 | + | return InstallRoute(c, config) | |||
| 34 | + | }) | |||
| 35 | + | ||||
| 29 | e.POST("/admin/install/traefik", handlers.TraefikInstallerHandler) | 36 | e.POST("/admin/install/traefik", handlers.TraefikInstallerHandler) | ||
| 30 | return e | 37 | return e | ||
| 31 | } | 38 | } | ||
| 32 | 39 | ||||
| 33 | - | func bindHtmxRoutes(e *echo.Echo) *echo.Echo { | 40 | + | func bindHtmxRoutes(e *echo.Echo, config config.Provider) *echo.Echo { |
| 34 | e.GET("/htmx", handlers.HTMXHandler) | 41 | e.GET("/htmx", handlers.HTMXHandler) | ||
| 35 | e.GET("/htmx/fragment", handlers.HTMXHandler) | 42 | e.GET("/htmx/fragment", handlers.HTMXHandler) | ||
| 36 | e.POST("/htmx", handlers.HTMXHandler) | 43 | e.POST("/htmx", handlers.HTMXHandler) | ||
| @@ -38,7 +45,10 @@ func bindHtmxRoutes(e *echo.Echo) *echo.Echo { | |||||
| 38 | return e | 45 | return e | ||
| 39 | } | 46 | } | ||
| 40 | 47 | ||||
| 41 | - | func bindStaticRoutes(e *echo.Echo) *echo.Echo { | 48 | + | func bindStaticRoutes(e *echo.Echo, config config.Provider) *echo.Echo { |
| 42 | - | e.GET("/*", StaticRoute) | 49 | + | |
| 50 | + | e.GET("/*", func(c echo.Context) error { | |||
| 51 | + | return StaticRoute(c, config) | |||
| 52 | + | }) | |||
| 43 | return e | 53 | return e | ||
| 44 | } | 54 | } | ||
M internal/http/routes/static-routes.go +10 -4
| @@ -1,19 +1,25 @@ | |||
| 1 | 1 | package routes | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | + | "io/fs" | |
| 5 | + | "net/http" | |
| 4 | 6 | "os" | |
| 5 | 7 | ||
| 6 | 8 | "github.com/labstack/echo/v4" | |
| 9 | + | "gogs.bnema.dev/gordon-echo/config" | |
| 7 | 10 | ) | |
| 8 | 11 | ||
| 9 | - | func (a *AppConfig) StaticRoute(c echo.Context) error { | |
| 10 | - | ||
| 11 | - | // Set the cache-control header to cache the static files for 1 day if PROD env is set to true | |
| 12 | + | func StaticRoute(c echo.Context, config config.Provider) error { | |
| 12 | 13 | ||
| 14 | + | // Set the cache-control header based on PROD environment variable | |
| 13 | 15 | if os.Getenv("PROD") == "true" { | |
| 14 | 16 | c.Response().Header().Set("Cache-Control", "public, max-age=86400") | |
| 15 | 17 | } else { | |
| 16 | 18 | c.Response().Header().Set("Cache-Control", "no-cache") | |
| 17 | 19 | } | |
| 18 | - | return echo.StaticDirectoryHandler(a.Config.GetPublicFS(), false)(c) | |
| 20 | + | ||
| 21 | + | // Serve the file from the embedded filesystem | |
| 22 | + | httpFs := http.FileServer(http.FS(fs.FS(config.GetPublicFS()))) | |
| 23 | + | httpFs.ServeHTTP(c.Response(), c.Request()) | |
| 24 | + | return nil | |
| 19 | 25 | } | |
| @@ -1,19 +1,25 @@ | |||||
| 1 | package routes | 1 | package routes | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | + | "io/fs" | |||
| 5 | + | "net/http" | |||
| 4 | "os" | 6 | "os" | ||
| 5 | 7 | ||||
| 6 | "github.com/labstack/echo/v4" | 8 | "github.com/labstack/echo/v4" | ||
| 9 | + | "gogs.bnema.dev/gordon-echo/config" | |||
| 7 | ) | 10 | ) | ||
| 8 | 11 | ||||
| 9 | - | func (a *AppConfig) StaticRoute(c echo.Context) error { | 12 | + | func StaticRoute(c echo.Context, config config.Provider) error { |
| 10 | - | ||||
| 11 | - | // Set the cache-control header to cache the static files for 1 day if PROD env is set to true | |||
| 12 | 13 | ||||
| 14 | + | // Set the cache-control header based on PROD environment variable | |||
| 13 | if os.Getenv("PROD") == "true" { | 15 | if os.Getenv("PROD") == "true" { | ||
| 14 | c.Response().Header().Set("Cache-Control", "public, max-age=86400") | 16 | c.Response().Header().Set("Cache-Control", "public, max-age=86400") | ||
| 15 | } else { | 17 | } else { | ||
| 16 | c.Response().Header().Set("Cache-Control", "no-cache") | 18 | c.Response().Header().Set("Cache-Control", "no-cache") | ||
| 17 | } | 19 | } | ||
| 18 | - | return echo.StaticDirectoryHandler(a.Config.GetPublicFS(), false)(c) | 20 | + | |
| 21 | + | // Serve the file from the embedded filesystem | |||
| 22 | + | httpFs := http.FileServer(http.FS(fs.FS(config.GetPublicFS()))) | |||
| 23 | + | httpFs.ServeHTTP(c.Response(), c.Request()) | |||
| 24 | + | return nil | |||
| 19 | } | 25 | } | ||
R internal/ui/templates/strings.yaml → internal/ui/templates/strings.yml +0 -0 renamed, 100% similar
renamed, 100% similar
M pkg/utils/html-fragments.go +2 -6
| @@ -8,14 +8,10 @@ import ( | |||
| 8 | 8 | "gogs.bnema.dev/gordon-echo/config" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | - | type AppConfig struct { | |
| 12 | - | Config config.Provider | |
| 13 | - | } | |
| 14 | - | ||
| 15 | 11 | // GetHTMLFragmentByID returns the HTML fragment with the specified id | |
| 16 | - | func (a *AppConfig) GetHTMLFragmentByID(id string, data interface{}) (string, error) { | |
| 12 | + | func GetHTMLFragmentByID(id string, data interface{}, fs config.Provider) (string, error) { | |
| 17 | 13 | // 1. Render the template | |
| 18 | - | renderer, err := GetRenderer("components.gohtml", a.Config.GetTemplateFS(), NewLogger()) | |
| 14 | + | renderer, err := GetRenderer("components.gohtml", fs.GetTemplateFS(), NewLogger()) | |
| 19 | 15 | if err != nil { | |
| 20 | 16 | return "", fmt.Errorf("failed to get renderer: %w", err) | |
| 21 | 17 | } | |
| @@ -8,14 +8,10 @@ import ( | |||||
| 8 | "gogs.bnema.dev/gordon-echo/config" | 8 | "gogs.bnema.dev/gordon-echo/config" | ||
| 9 | ) | 9 | ) | ||
| 10 | 10 | ||||
| 11 | - | type AppConfig struct { | |||
| 12 | - | Config config.Provider | |||
| 13 | - | } | |||
| 14 | - | ||||
| 15 | // GetHTMLFragmentByID returns the HTML fragment with the specified id | 11 | // GetHTMLFragmentByID returns the HTML fragment with the specified id | ||
| 16 | - | func (a *AppConfig) GetHTMLFragmentByID(id string, data interface{}) (string, error) { | 12 | + | func GetHTMLFragmentByID(id string, data interface{}, fs config.Provider) (string, error) { |
| 17 | // 1. Render the template | 13 | // 1. Render the template | ||
| 18 | - | renderer, err := GetRenderer("components.gohtml", a.Config.GetTemplateFS(), NewLogger()) | 14 | + | renderer, err := GetRenderer("components.gohtml", fs.GetTemplateFS(), NewLogger()) |
| 19 | if err != nil { | 15 | if err != nil { | ||
| 20 | return "", fmt.Errorf("failed to get renderer: %w", err) | 16 | return "", fmt.Errorf("failed to get renderer: %w", err) | ||
| 21 | } | 17 | } | ||
M pkg/utils/html-render.go +2 -0
| @@ -3,6 +3,7 @@ package utils | |||
| 3 | 3 | import ( | |
| 4 | 4 | "bytes" | |
| 5 | 5 | "errors" | |
| 6 | + | "fmt" | |
| 6 | 7 | "html/template" | |
| 7 | 8 | "io/fs" | |
| 8 | 9 | ) | |
| @@ -39,6 +40,7 @@ func (r *Renderer) Render(data interface{}) (string, error) { | |||
| 39 | 40 | ||
| 40 | 41 | // GetRenderer function returns a new Renderer instance | |
| 41 | 42 | func GetRenderer(filename string, fs fs.FS, logger *Logger) (*Renderer, error) { | |
| 43 | + | fmt.Println(filename) | |
| 42 | 44 | // Check if the file exists in the provided fs.FS using fs.Open | |
| 43 | 45 | file, err := fs.Open(filename) | |
| 44 | 46 | if err != nil { | |
| @@ -3,6 +3,7 @@ package utils | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "bytes" | 4 | "bytes" | ||
| 5 | "errors" | 5 | "errors" | ||
| 6 | + | "fmt" | |||
| 6 | "html/template" | 7 | "html/template" | ||
| 7 | "io/fs" | 8 | "io/fs" | ||
| 8 | ) | 9 | ) | ||
| @@ -39,6 +40,7 @@ func (r *Renderer) Render(data interface{}) (string, error) { | |||||
| 39 | 40 | ||||
| 40 | // GetRenderer function returns a new Renderer instance | 41 | // GetRenderer function returns a new Renderer instance | ||
| 41 | func GetRenderer(filename string, fs fs.FS, logger *Logger) (*Renderer, error) { | 42 | func GetRenderer(filename string, fs fs.FS, logger *Logger) (*Renderer, error) { | ||
| 43 | + | fmt.Println(filename) | |||
| 42 | // Check if the file exists in the provided fs.FS using fs.Open | 44 | // Check if the file exists in the provided fs.FS using fs.Open | ||
| 43 | file, err := fs.Open(filename) | 45 | file, err := fs.Open(filename) | ||
| 44 | if err != nil { | 46 | if err != nil { | ||
M pkg/utils/parser.go +44 -24
| @@ -3,6 +3,7 @@ package utils | |||
| 3 | 3 | import ( | |
| 4 | 4 | "fmt" | |
| 5 | 5 | "io" | |
| 6 | + | "io/fs" | |
| 6 | 7 | "os" | |
| 7 | 8 | ||
| 8 | 9 | "github.com/tidwall/gjson" | |
| @@ -11,52 +12,71 @@ import ( | |||
| 11 | 12 | ||
| 12 | 13 | // Yaml Parser | |
| 13 | 14 | type LangData map[string]interface{} | |
| 14 | - | type YamlData struct { | |
| 15 | - | EN LangData `yaml:"en"` | |
| 16 | - | FR LangData `yaml:"fr"` | |
| 17 | - | CurrentLang LangData | |
| 15 | + | ||
| 16 | + | type StringsYamlData struct { | |
| 17 | + | StringsModel StringsModel `yaml:"strings_model"` | |
| 18 | + | EN LangData `yaml:"en"` | |
| 19 | + | FR LangData `yaml:"fr"` | |
| 20 | + | CurrentLang LangData | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | type ComposeModelData struct { | |
| 24 | + | ComposeModel ComposeModel `yaml:"compose_model"` | |
| 18 | 25 | } | |
| 19 | 26 | ||
| 20 | - | // ReadDataFromYAML loads the data from the given YAML file | |
| 21 | - | func (a *AppConfig) ReadDataFromYAML() (YamlData, error) { | |
| 22 | - | var data YamlData | |
| 27 | + | type StringsModel struct { | |
| 28 | + | Header string `yaml:"header"` | |
| 29 | + | Subheader string `yaml:"subheader"` | |
| 30 | + | } | |
| 31 | + | ||
| 32 | + | type ComposeModel struct { | |
| 33 | + | Header string `yaml:"header"` | |
| 34 | + | Subheader string `yaml:"subheader"` | |
| 35 | + | } | |
| 23 | 36 | ||
| 24 | - | // Open the YAML file inside the ui/components directory | |
| 25 | - | file, err := a.Config.GetTemplateFS().Open("strings.yaml") | |
| 37 | + | // ReadDataFromYAML loads the data from the given YAML file from any filesystem | |
| 38 | + | func ReadDataFromYAML(fs fs.FS, filename string, out interface{}) error { | |
| 39 | + | // Open the YAML file | |
| 40 | + | file, err := fs.Open(filename) | |
| 26 | 41 | if err != nil { | |
| 27 | - | return data, fmt.Errorf("failed to open strings.yaml: %w", err) | |
| 42 | + | return fmt.Errorf("failed to open %s: %w", filename, err) | |
| 28 | 43 | } | |
| 29 | 44 | defer file.Close() | |
| 30 | 45 | ||
| 31 | 46 | // Read the content of the file | |
| 32 | 47 | content, err := io.ReadAll(file) | |
| 33 | 48 | if err != nil { | |
| 34 | - | return data, fmt.Errorf("failed to read strings.yaml: %w", err) | |
| 49 | + | return fmt.Errorf("failed to read %s: %w", filename, err) | |
| 35 | 50 | } | |
| 36 | 51 | ||
| 37 | - | // Unmarshal the YAML content into the Data struct | |
| 38 | - | if err := yaml.Unmarshal(content, &data); err != nil { | |
| 39 | - | return data, fmt.Errorf("failed to unmarshal strings.yaml: %w", err) | |
| 52 | + | // Unmarshal the YAML content into the out interface | |
| 53 | + | if err := yaml.Unmarshal(content, out); err != nil { | |
| 54 | + | return fmt.Errorf("failed to unmarshal %s: %w", filename, err) | |
| 40 | 55 | } | |
| 41 | 56 | ||
| 42 | - | return data, nil | |
| 57 | + | return nil | |
| 43 | 58 | } | |
| 44 | 59 | ||
| 45 | 60 | // PopulateDataFromYAML retrieves the data from the YAML file and sets the appropriate language data | |
| 46 | - | func (a *AppConfig) PopulateDataFromYAML(currentLang string) (YamlData, error) { | |
| 47 | - | data, err := a.ReadDataFromYAML() | |
| 61 | + | func PopulateDataFromYAML(lang string, fs fs.FS, filename string, out interface{}) error { | |
| 62 | + | err := ReadDataFromYAML(fs, filename, out) | |
| 48 | 63 | if err != nil { | |
| 49 | - | return data, err | |
| 64 | + | return fmt.Errorf("failed to read data from YAML: %w", err) | |
| 50 | 65 | } | |
| 51 | 66 | ||
| 52 | - | switch currentLang { | |
| 53 | - | case "fr": | |
| 54 | - | data.CurrentLang = data.FR | |
| 55 | - | default: // default to English if no match | |
| 56 | - | data.CurrentLang = data.EN | |
| 67 | + | // If the type assertion succeeds, we set the appropriate language data | |
| 68 | + | if data, ok := out.(*StringsYamlData); ok { | |
| 69 | + | switch lang { | |
| 70 | + | case "en": | |
| 71 | + | data.CurrentLang = data.EN | |
| 72 | + | case "fr": | |
| 73 | + | data.CurrentLang = data.FR | |
| 74 | + | default: // default to English if no match | |
| 75 | + | data.CurrentLang = data.EN | |
| 76 | + | } | |
| 57 | 77 | } | |
| 58 | 78 | ||
| 59 | - | return data, nil | |
| 79 | + | return nil | |
| 60 | 80 | } | |
| 61 | 81 | ||
| 62 | 82 | // Json Parser | |
| @@ -3,6 +3,7 @@ package utils | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "fmt" | 4 | "fmt" | ||
| 5 | "io" | 5 | "io" | ||
| 6 | + | "io/fs" | |||
| 6 | "os" | 7 | "os" | ||
| 7 | 8 | ||||
| 8 | "github.com/tidwall/gjson" | 9 | "github.com/tidwall/gjson" | ||
| @@ -11,52 +12,71 @@ import ( | |||||
| 11 | 12 | ||||
| 12 | // Yaml Parser | 13 | // Yaml Parser | ||
| 13 | type LangData map[string]interface{} | 14 | type LangData map[string]interface{} | ||
| 14 | - | type YamlData struct { | 15 | + | |
| 15 | - | EN LangData `yaml:"en"` | 16 | + | type StringsYamlData struct { |
| 16 | - | FR LangData `yaml:"fr"` | 17 | + | StringsModel StringsModel `yaml:"strings_model"` |
| 17 | - | CurrentLang LangData | 18 | + | EN LangData `yaml:"en"` |
| 19 | + | FR LangData `yaml:"fr"` | |||
| 20 | + | CurrentLang LangData | |||
| 21 | + | } | |||
| 22 | + | ||||
| 23 | + | type ComposeModelData struct { | |||
| 24 | + | ComposeModel ComposeModel `yaml:"compose_model"` | |||
| 18 | } | 25 | } | ||
| 19 | 26 | ||||
| 20 | - | // ReadDataFromYAML loads the data from the given YAML file | 27 | + | type StringsModel struct { |
| 21 | - | func (a *AppConfig) ReadDataFromYAML() (YamlData, error) { | 28 | + | Header string `yaml:"header"` |
| 22 | - | var data YamlData | 29 | + | Subheader string `yaml:"subheader"` |
| 30 | + | } | |||
| 31 | + | ||||
| 32 | + | type ComposeModel struct { | |||
| 33 | + | Header string `yaml:"header"` | |||
| 34 | + | Subheader string `yaml:"subheader"` | |||
| 35 | + | } | |||
| 23 | 36 | ||||
| 24 | - | // Open the YAML file inside the ui/components directory | 37 | + | // ReadDataFromYAML loads the data from the given YAML file from any filesystem |
| 25 | - | file, err := a.Config.GetTemplateFS().Open("strings.yaml") | 38 | + | func ReadDataFromYAML(fs fs.FS, filename string, out interface{}) error { |
| 39 | + | // Open the YAML file | |||
| 40 | + | file, err := fs.Open(filename) | |||
| 26 | if err != nil { | 41 | if err != nil { | ||
| 27 | - | return data, fmt.Errorf("failed to open strings.yaml: %w", err) | 42 | + | return fmt.Errorf("failed to open %s: %w", filename, err) |
| 28 | } | 43 | } | ||
| 29 | defer file.Close() | 44 | defer file.Close() | ||
| 30 | 45 | ||||
| 31 | // Read the content of the file | 46 | // Read the content of the file | ||
| 32 | content, err := io.ReadAll(file) | 47 | content, err := io.ReadAll(file) | ||
| 33 | if err != nil { | 48 | if err != nil { | ||
| 34 | - | return data, fmt.Errorf("failed to read strings.yaml: %w", err) | 49 | + | return fmt.Errorf("failed to read %s: %w", filename, err) |
| 35 | } | 50 | } | ||
| 36 | 51 | ||||
| 37 | - | // Unmarshal the YAML content into the Data struct | 52 | + | // Unmarshal the YAML content into the out interface |
| 38 | - | if err := yaml.Unmarshal(content, &data); err != nil { | 53 | + | if err := yaml.Unmarshal(content, out); err != nil { |
| 39 | - | return data, fmt.Errorf("failed to unmarshal strings.yaml: %w", err) | 54 | + | return fmt.Errorf("failed to unmarshal %s: %w", filename, err) |
| 40 | } | 55 | } | ||
| 41 | 56 | ||||
| 42 | - | return data, nil | 57 | + | return nil |
| 43 | } | 58 | } | ||
| 44 | 59 | ||||
| 45 | // PopulateDataFromYAML retrieves the data from the YAML file and sets the appropriate language data | 60 | // PopulateDataFromYAML retrieves the data from the YAML file and sets the appropriate language data | ||
| 46 | - | func (a *AppConfig) PopulateDataFromYAML(currentLang string) (YamlData, error) { | 61 | + | func PopulateDataFromYAML(lang string, fs fs.FS, filename string, out interface{}) error { |
| 47 | - | data, err := a.ReadDataFromYAML() | 62 | + | err := ReadDataFromYAML(fs, filename, out) |
| 48 | if err != nil { | 63 | if err != nil { | ||
| 49 | - | return data, err | 64 | + | return fmt.Errorf("failed to read data from YAML: %w", err) |
| 50 | } | 65 | } | ||
| 51 | 66 | ||||
| 52 | - | switch currentLang { | 67 | + | // If the type assertion succeeds, we set the appropriate language data |
| 53 | - | case "fr": | 68 | + | if data, ok := out.(*StringsYamlData); ok { |
| 54 | - | data.CurrentLang = data.FR | 69 | + | switch lang { |
| 55 | - | default: // default to English if no match | 70 | + | case "en": |
| 56 | - | data.CurrentLang = data.EN | 71 | + | data.CurrentLang = data.EN |
| 72 | + | case "fr": | |||
| 73 | + | data.CurrentLang = data.FR | |||
| 74 | + | default: // default to English if no match | |||
| 75 | + | data.CurrentLang = data.EN | |||
| 76 | + | } | |||
| 57 | } | 77 | } | ||
| 58 | 78 | ||||
| 59 | - | return data, nil | 79 | + | return nil |
| 60 | } | 80 | } | ||
| 61 | 81 | ||||
| 62 | // Json Parser | 82 | // Json Parser | ||
D pkg/utils/txt-render.go +0 -60
| @@ -1,60 +0,0 @@ | |||
| 1 | - | package utils | |
| 2 | - | ||
| 3 | - | import ( | |
| 4 | - | "bytes" | |
| 5 | - | "errors" | |
| 6 | - | "io/fs" | |
| 7 | - | "text/template" | |
| 8 | - | ) | |
| 9 | - | ||
| 10 | - | type TXTRenderer struct { | |
| 11 | - | Models *template.Template | |
| 12 | - | Format string | |
| 13 | - | ParseError error | |
| 14 | - | Logger *Logger | |
| 15 | - | } | |
| 16 | - | ||
| 17 | - | // Render function renders the template with the given data | |
| 18 | - | func (r *TXTRenderer) TXTRender(data interface{}) (string, error) { | |
| 19 | - | if r.ParseError != nil { | |
| 20 | - | r.Logger.Error().Err(r.ParseError).Msg("Parse error") | |
| 21 | - | return "", r.ParseError | |
| 22 | - | } | |
| 23 | - | ||
| 24 | - | if r.Models == nil { | |
| 25 | - | err := errors.New("invalid or nil template") | |
| 26 | - | r.Logger.Error().Err(r.ParseError).Msg("Parse error") | |
| 27 | - | ||
| 28 | - | return "", err | |
| 29 | - | } | |
| 30 | - | ||
| 31 | - | buf := new(bytes.Buffer) | |
| 32 | - | err := r.Models.Execute(buf, data) | |
| 33 | - | if err != nil { | |
| 34 | - | r.Logger.Error().Err(err).Msg("Render error") | |
| 35 | - | return "", err | |
| 36 | - | } | |
| 37 | - | ||
| 38 | - | return buf.String(), nil | |
| 39 | - | } | |
| 40 | - | ||
| 41 | - | // GetTXTRenderer function returns a new Renderer instance | |
| 42 | - | func GetTXTRenderer(filename string, fs fs.FS, logger *Logger) (*TXTRenderer, error) { | |
| 43 | - | // Check if the file exists in the provided fs.FS using fs.Open | |
| 44 | - | file, err := fs.Open(filename) | |
| 45 | - | if err != nil { | |
| 46 | - | logger.Error().Err(err).Msg("Template or model '%s' not found" + filename) | |
| 47 | - | return nil, err | |
| 48 | - | } | |
| 49 | - | file.Close() // Close the file after checking | |
| 50 | - | ||
| 51 | - | mdls, err := template.New(filename).ParseFS(fs, filename) | |
| 52 | - | if err != nil { | |
| 53 | - | logger.Error().Err(err).Msg("Failed to parse template") | |
| 54 | - | return nil, err | |
| 55 | - | } | |
| 56 | - | return &TXTRenderer{ | |
| 57 | - | Models: mdls, | |
| 58 | - | Logger: logger, | |
| 59 | - | }, nil | |
| 60 | - | } | |
| @@ -1,60 +0,0 @@ | |||||
| 1 | - | package utils | |||
| 2 | - | ||||
| 3 | - | import ( | |||
| 4 | - | "bytes" | |||
| 5 | - | "errors" | |||
| 6 | - | "io/fs" | |||
| 7 | - | "text/template" | |||
| 8 | - | ) | |||
| 9 | - | ||||
| 10 | - | type TXTRenderer struct { | |||
| 11 | - | Models *template.Template | |||
| 12 | - | Format string | |||
| 13 | - | ParseError error | |||
| 14 | - | Logger *Logger | |||
| 15 | - | } | |||
| 16 | - | ||||
| 17 | - | // Render function renders the template with the given data | |||
| 18 | - | func (r *TXTRenderer) TXTRender(data interface{}) (string, error) { | |||
| 19 | - | if r.ParseError != nil { | |||
| 20 | - | r.Logger.Error().Err(r.ParseError).Msg("Parse error") | |||
| 21 | - | return "", r.ParseError | |||
| 22 | - | } | |||
| 23 | - | ||||
| 24 | - | if r.Models == nil { | |||
| 25 | - | err := errors.New("invalid or nil template") | |||
| 26 | - | r.Logger.Error().Err(r.ParseError).Msg("Parse error") | |||
| 27 | - | ||||
| 28 | - | return "", err | |||
| 29 | - | } | |||
| 30 | - | ||||
| 31 | - | buf := new(bytes.Buffer) | |||
| 32 | - | err := r.Models.Execute(buf, data) | |||
| 33 | - | if err != nil { | |||
| 34 | - | r.Logger.Error().Err(err).Msg("Render error") | |||
| 35 | - | return "", err | |||
| 36 | - | } | |||
| 37 | - | ||||
| 38 | - | return buf.String(), nil | |||
| 39 | - | } | |||
| 40 | - | ||||
| 41 | - | // GetTXTRenderer function returns a new Renderer instance | |||
| 42 | - | func GetTXTRenderer(filename string, fs fs.FS, logger *Logger) (*TXTRenderer, error) { | |||
| 43 | - | // Check if the file exists in the provided fs.FS using fs.Open | |||
| 44 | - | file, err := fs.Open(filename) | |||
| 45 | - | if err != nil { | |||
| 46 | - | logger.Error().Err(err).Msg("Template or model '%s' not found" + filename) | |||
| 47 | - | return nil, err | |||
| 48 | - | } | |||
| 49 | - | file.Close() // Close the file after checking | |||
| 50 | - | ||||
| 51 | - | mdls, err := template.New(filename).ParseFS(fs, filename) | |||
| 52 | - | if err != nil { | |||
| 53 | - | logger.Error().Err(err).Msg("Failed to parse template") | |||
| 54 | - | return nil, err | |||
| 55 | - | } | |||
| 56 | - | return &TXTRenderer{ | |||
| 57 | - | Models: mdls, | |||
| 58 | - | Logger: logger, | |||
| 59 | - | }, nil | |||
| 60 | - | } | |||