WIP
- commit
- 22ef2df1e4cd84e6977a6c24055f49b89f0a64ea
- author
- bnema · · 2023-09-05 16:43 UTC
- parent
- fcbb900
- signature
- unsigned
21 files · +256 -89
D .env +0 -0
no textual change.
A .env.example +7 -0
| @@ -0,0 +1,7 @@ | |||
| 1 | + | ENV=EITHER PRODUCTION OR DEVELOPMENT | |
| 2 | + | LOG_LEVEL= DEBUG, INFO, WARN, ERROR | |
| 3 | + | PORT= DEFAULT 1323 | |
| 4 | + | ABSOLUTE_PATH= ABSOLUTE INSTALL PATH OF THE APP | |
| 5 | + | TOP_DOMAIN= EXAMPLE.COM | |
| 6 | + | GORDON_DOMAIN= GORDON.EXAMPLE.COM | |
| 7 | + | ADMIN_EMAIL= EMAIL ADDRESS OF THE ADMIN⏎̸ | |
| @@ -0,0 +1,7 @@ | |||||
| 1 | + | ENV=EITHER PRODUCTION OR DEVELOPMENT | |||
| 2 | + | LOG_LEVEL= DEBUG, INFO, WARN, ERROR | |||
| 3 | + | PORT= DEFAULT 1323 | |||
| 4 | + | ABSOLUTE_PATH= ABSOLUTE INSTALL PATH OF THE APP | |||
| 5 | + | TOP_DOMAIN= EXAMPLE.COM | |||
| 6 | + | GORDON_DOMAIN= GORDON.EXAMPLE.COM | |||
| 7 | + | ADMIN_EMAIL= EMAIL ADDRESS OF THE ADMIN⏎̸ | |||
M cmd/main.go +8 -1
| @@ -1,9 +1,11 @@ | |||
| 1 | 1 | package main | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | + | "embed" | |
| 4 | 5 | "os" | |
| 5 | 6 | ||
| 6 | 7 | "github.com/labstack/echo/v4" | |
| 8 | + | root "gogs.bnema.dev/gordon-echo" | |
| 7 | 9 | "gogs.bnema.dev/gordon-echo/internal/app" | |
| 8 | 10 | "gogs.bnema.dev/gordon-echo/internal/http/routes" | |
| 9 | 11 | "gogs.bnema.dev/gordon-echo/pkg/utils" | |
| @@ -16,8 +18,13 @@ func main() { | |||
| 16 | 18 | port = "1323" | |
| 17 | 19 | } | |
| 18 | 20 | ||
| 21 | + | config := app.Config{ | |
| 22 | + | TemplateFS: root.TemplateFS.(embed.FS), | |
| 23 | + | PublicFS: root.PublicFS.(embed.FS), | |
| 24 | + | ModelFS: root.ModelFS, | |
| 25 | + | } | |
| 19 | 26 | // Initialize Gordon | |
| 20 | - | gordon, err := app.NewApp() | |
| 27 | + | gordon, err := app.NewApp(&config) | |
| 21 | 28 | if err != nil { | |
| 22 | 29 | // Handle initialization error and exit | |
| 23 | 30 | gordon.AppLogger.Error().Err(err).Msg("Failed to initialize app") | |
| @@ -1,9 +1,11 @@ | |||||
| 1 | package main | 1 | package main | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | + | "embed" | |||
| 4 | "os" | 5 | "os" | ||
| 5 | 6 | ||||
| 6 | "github.com/labstack/echo/v4" | 7 | "github.com/labstack/echo/v4" | ||
| 8 | + | root "gogs.bnema.dev/gordon-echo" | |||
| 7 | "gogs.bnema.dev/gordon-echo/internal/app" | 9 | "gogs.bnema.dev/gordon-echo/internal/app" | ||
| 8 | "gogs.bnema.dev/gordon-echo/internal/http/routes" | 10 | "gogs.bnema.dev/gordon-echo/internal/http/routes" | ||
| 9 | "gogs.bnema.dev/gordon-echo/pkg/utils" | 11 | "gogs.bnema.dev/gordon-echo/pkg/utils" | ||
| @@ -16,8 +18,13 @@ func main() { | |||||
| 16 | port = "1323" | 18 | port = "1323" | ||
| 17 | } | 19 | } | ||
| 18 | 20 | ||||
| 21 | + | config := app.Config{ | |||
| 22 | + | TemplateFS: root.TemplateFS.(embed.FS), | |||
| 23 | + | PublicFS: root.PublicFS.(embed.FS), | |||
| 24 | + | ModelFS: root.ModelFS, | |||
| 25 | + | } | |||
| 19 | // Initialize Gordon | 26 | // Initialize Gordon | ||
| 20 | - | gordon, err := app.NewApp() | 27 | + | gordon, err := app.NewApp(&config) |
| 21 | if err != nil { | 28 | if err != nil { | ||
| 22 | // Handle initialization error and exit | 29 | // Handle initialization error and exit | ||
| 23 | gordon.AppLogger.Error().Err(err).Msg("Failed to initialize app") | 30 | gordon.AppLogger.Error().Err(err).Msg("Failed to initialize app") | ||
A config/config-provider.go +9 -0
| @@ -0,0 +1,9 @@ | |||
| 1 | + | package config | |
| 2 | + | ||
| 3 | + | import "embed" | |
| 4 | + | ||
| 5 | + | type Provider interface { | |
| 6 | + | GetTemplateFS() embed.FS | |
| 7 | + | GetPublicFS() embed.FS | |
| 8 | + | GetModelFS() embed.FS | |
| 9 | + | } | |
| @@ -0,0 +1,9 @@ | |||||
| 1 | + | package config | |||
| 2 | + | ||||
| 3 | + | import "embed" | |||
| 4 | + | ||||
| 5 | + | type Provider interface { | |||
| 6 | + | GetTemplateFS() embed.FS | |||
| 7 | + | GetPublicFS() embed.FS | |||
| 8 | + | GetModelFS() embed.FS | |||
| 9 | + | } | |||
A embed.go +26 -0
| @@ -0,0 +1,26 @@ | |||
| 1 | + | package embed | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "embed" | |
| 5 | + | ||
| 6 | + | "github.com/labstack/echo/v4" | |
| 7 | + | ) | |
| 8 | + | ||
| 9 | + | // Embedding the public and templates directories | |
| 10 | + | ||
| 11 | + | //go:embed internal/ui/public/* | |
| 12 | + | var public embed.FS | |
| 13 | + | ||
| 14 | + | //go:embed internal/ui/templates/* | |
| 15 | + | var templates embed.FS | |
| 16 | + | ||
| 17 | + | // Embedding the YAML models | |
| 18 | + | // | |
| 19 | + | //go:embed pkg/templating/models/* | |
| 20 | + | var modelsEmbed embed.FS | |
| 21 | + | ||
| 22 | + | var PublicFS = echo.MustSubFS(public, "internal/ui/public") | |
| 23 | + | 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 | |
| @@ -0,0 +1,26 @@ | |||||
| 1 | + | package embed | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "embed" | |||
| 5 | + | ||||
| 6 | + | "github.com/labstack/echo/v4" | |||
| 7 | + | ) | |||
| 8 | + | ||||
| 9 | + | // Embedding the public and templates directories | |||
| 10 | + | ||||
| 11 | + | //go:embed internal/ui/public/* | |||
| 12 | + | var public embed.FS | |||
| 13 | + | ||||
| 14 | + | //go:embed internal/ui/templates/* | |||
| 15 | + | var templates embed.FS | |||
| 16 | + | ||||
| 17 | + | // Embedding the YAML models | |||
| 18 | + | // | |||
| 19 | + | //go:embed pkg/templating/models/* | |||
| 20 | + | var modelsEmbed embed.FS | |||
| 21 | + | ||||
| 22 | + | var PublicFS = echo.MustSubFS(public, "internal/ui/public") | |||
| 23 | + | 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 | |||
M internal/app/app.go +35 -21
| @@ -1,17 +1,16 @@ | |||
| 1 | 1 | package app | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | - | "html/template" | |
| 4 | + | "embed" | |
| 5 | 5 | "io" | |
| 6 | 6 | "os" | |
| 7 | 7 | "path/filepath" | |
| 8 | 8 | ||
| 9 | 9 | "github.com/rs/zerolog" | |
| 10 | + | "gogs.bnema.dev/gordon-echo/config" | |
| 10 | 11 | "gogs.bnema.dev/gordon-echo/pkg/utils" | |
| 11 | 12 | ) | |
| 12 | 13 | ||
| 13 | - | var cfg = loadConfig() | |
| 14 | - | ||
| 15 | 14 | const ( | |
| 16 | 15 | DefaultBuildDir = "tmp" | |
| 17 | 16 | DefaultLogDir = "logs" | |
| @@ -24,14 +23,30 @@ const ( | |||
| 24 | 23 | type App struct { | |
| 25 | 24 | AppLogger *utils.Logger | |
| 26 | 25 | HttpLogger *utils.Logger | |
| 27 | - | Template *template.Template | |
| 28 | 26 | } | |
| 29 | 27 | type Config struct { | |
| 30 | - | BuildDir string | |
| 31 | - | LogDir string | |
| 28 | + | BuildDir string | |
| 29 | + | LogDir string | |
| 30 | + | TemplateFS embed.FS | |
| 31 | + | PublicFS embed.FS | |
| 32 | + | ModelFS embed.FS | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | func (c *Config) GetTemplateFS() embed.FS { | |
| 36 | + | return c.TemplateFS | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | func (c *Config) GetPublicFS() embed.FS { | |
| 40 | + | return c.PublicFS | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | func (c *Config) GetModelFS() embed.FS { | |
| 44 | + | return c.ModelFS | |
| 32 | 45 | } | |
| 33 | 46 | ||
| 34 | - | func loadConfig() Config { | |
| 47 | + | var _ config.Provider = &Config{} | |
| 48 | + | ||
| 49 | + | func loadConfig(templateFS, publicFS, modelFS embed.FS) Config { | |
| 35 | 50 | // Get the environment variable | |
| 36 | 51 | env := os.Getenv(EnvVarName) | |
| 37 | 52 | // If the environment variable is empty, considere it is dev -> (/tmp) | |
| @@ -56,25 +71,24 @@ func loadConfig() Config { | |||
| 56 | 71 | } | |
| 57 | 72 | ||
| 58 | 73 | return Config{ | |
| 59 | - | BuildDir: buildDir, | |
| 60 | - | LogDir: logDir, | |
| 74 | + | BuildDir: buildDir, | |
| 75 | + | LogDir: logDir, | |
| 76 | + | TemplateFS: templateFS, | |
| 77 | + | PublicFS: publicFS, | |
| 78 | + | ModelFS: modelFS, | |
| 61 | 79 | } | |
| 62 | 80 | } | |
| 63 | - | func NewApp() (*App, error) { | |
| 64 | - | SetupLogging() | |
| 65 | - | app := &App{} | |
| 66 | - | ||
| 67 | - | // Initialize the general application logger | |
| 68 | - | app.AppLogger = utils.NewLogger().SetOutput(initializeLoggerOutput("app.log")) | |
| 69 | - | // Initialize the HTTP logger | |
| 70 | - | app.HttpLogger = utils.NewLogger().SetOutput(initializeLoggerOutput("http.log")) | |
| 71 | - | ||
| 81 | + | func NewApp(config *Config) (*App, error) { | |
| 82 | + | SetupLogging(config) | |
| 83 | + | app := &App{ | |
| 84 | + | AppLogger: utils.NewLogger().SetOutput(initializeLoggerOutput(config, "app.log")), | |
| 85 | + | HttpLogger: utils.NewLogger().SetOutput(initializeLoggerOutput(config, "http.log")), | |
| 86 | + | } | |
| 72 | 87 | return app, nil | |
| 73 | 88 | } | |
| 74 | 89 | ||
| 75 | - | func initializeLoggerOutput(logFile string) io.Writer { | |
| 90 | + | func initializeLoggerOutput(cfg *Config, logFile string) io.Writer { | |
| 76 | 91 | logPath := filepath.Join(cfg.LogDir, logFile) | |
| 77 | - | ||
| 78 | 92 | dir := filepath.Dir(logPath) | |
| 79 | 93 | if _, err := os.Stat(dir); os.IsNotExist(err) { | |
| 80 | 94 | if mkdirErr := os.MkdirAll(dir, 0755); mkdirErr != nil { | |
| @@ -90,7 +104,7 @@ func initializeLoggerOutput(logFile string) io.Writer { | |||
| 90 | 104 | return zerolog.MultiLevelWriter(file, consoleWriter) | |
| 91 | 105 | } | |
| 92 | 106 | ||
| 93 | - | func SetupLogging() { | |
| 107 | + | func SetupLogging(cfg *Config) { | |
| 94 | 108 | err := utils.CreateLogsDir(cfg.LogDir) | |
| 95 | 109 | if err != nil { | |
| 96 | 110 | panic(err) | |
| @@ -1,17 +1,16 @@ | |||||
| 1 | package app | 1 | package app | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | - | "html/template" | 4 | + | "embed" |
| 5 | "io" | 5 | "io" | ||
| 6 | "os" | 6 | "os" | ||
| 7 | "path/filepath" | 7 | "path/filepath" | ||
| 8 | 8 | ||||
| 9 | "github.com/rs/zerolog" | 9 | "github.com/rs/zerolog" | ||
| 10 | + | "gogs.bnema.dev/gordon-echo/config" | |||
| 10 | "gogs.bnema.dev/gordon-echo/pkg/utils" | 11 | "gogs.bnema.dev/gordon-echo/pkg/utils" | ||
| 11 | ) | 12 | ) | ||
| 12 | 13 | ||||
| 13 | - | var cfg = loadConfig() | |||
| 14 | - | ||||
| 15 | const ( | 14 | const ( | ||
| 16 | DefaultBuildDir = "tmp" | 15 | DefaultBuildDir = "tmp" | ||
| 17 | DefaultLogDir = "logs" | 16 | DefaultLogDir = "logs" | ||
| @@ -24,14 +23,30 @@ const ( | |||||
| 24 | type App struct { | 23 | type App struct { | ||
| 25 | AppLogger *utils.Logger | 24 | AppLogger *utils.Logger | ||
| 26 | HttpLogger *utils.Logger | 25 | HttpLogger *utils.Logger | ||
| 27 | - | Template *template.Template | |||
| 28 | } | 26 | } | ||
| 29 | type Config struct { | 27 | type Config struct { | ||
| 30 | - | BuildDir string | 28 | + | BuildDir string |
| 31 | - | LogDir string | 29 | + | LogDir string |
| 30 | + | TemplateFS embed.FS | |||
| 31 | + | PublicFS embed.FS | |||
| 32 | + | ModelFS embed.FS | |||
| 33 | + | } | |||
| 34 | + | ||||
| 35 | + | func (c *Config) GetTemplateFS() embed.FS { | |||
| 36 | + | return c.TemplateFS | |||
| 37 | + | } | |||
| 38 | + | ||||
| 39 | + | func (c *Config) GetPublicFS() embed.FS { | |||
| 40 | + | return c.PublicFS | |||
| 41 | + | } | |||
| 42 | + | ||||
| 43 | + | func (c *Config) GetModelFS() embed.FS { | |||
| 44 | + | return c.ModelFS | |||
| 32 | } | 45 | } | ||
| 33 | 46 | ||||
| 34 | - | func loadConfig() Config { | 47 | + | var _ config.Provider = &Config{} |
| 48 | + | ||||
| 49 | + | func loadConfig(templateFS, publicFS, modelFS embed.FS) Config { | |||
| 35 | // Get the environment variable | 50 | // Get the environment variable | ||
| 36 | env := os.Getenv(EnvVarName) | 51 | env := os.Getenv(EnvVarName) | ||
| 37 | // If the environment variable is empty, considere it is dev -> (/tmp) | 52 | // If the environment variable is empty, considere it is dev -> (/tmp) | ||
| @@ -56,25 +71,24 @@ func loadConfig() Config { | |||||
| 56 | } | 71 | } | ||
| 57 | 72 | ||||
| 58 | return Config{ | 73 | return Config{ | ||
| 59 | - | BuildDir: buildDir, | 74 | + | BuildDir: buildDir, |
| 60 | - | LogDir: logDir, | 75 | + | LogDir: logDir, |
| 76 | + | TemplateFS: templateFS, | |||
| 77 | + | PublicFS: publicFS, | |||
| 78 | + | ModelFS: modelFS, | |||
| 61 | } | 79 | } | ||
| 62 | } | 80 | } | ||
| 63 | - | func NewApp() (*App, error) { | 81 | + | func NewApp(config *Config) (*App, error) { |
| 64 | - | SetupLogging() | 82 | + | SetupLogging(config) |
| 65 | - | app := &App{} | 83 | + | app := &App{ |
| 66 | - | 84 | + | AppLogger: utils.NewLogger().SetOutput(initializeLoggerOutput(config, "app.log")), | |
| 67 | - | // Initialize the general application logger | 85 | + | HttpLogger: utils.NewLogger().SetOutput(initializeLoggerOutput(config, "http.log")), |
| 68 | - | app.AppLogger = utils.NewLogger().SetOutput(initializeLoggerOutput("app.log")) | 86 | + | } |
| 69 | - | // Initialize the HTTP logger | |||
| 70 | - | app.HttpLogger = utils.NewLogger().SetOutput(initializeLoggerOutput("http.log")) | |||
| 71 | - | ||||
| 72 | return app, nil | 87 | return app, nil | ||
| 73 | } | 88 | } | ||
| 74 | 89 | ||||
| 75 | - | func initializeLoggerOutput(logFile string) io.Writer { | 90 | + | func initializeLoggerOutput(cfg *Config, logFile string) io.Writer { |
| 76 | logPath := filepath.Join(cfg.LogDir, logFile) | 91 | logPath := filepath.Join(cfg.LogDir, logFile) | ||
| 77 | - | ||||
| 78 | dir := filepath.Dir(logPath) | 92 | dir := filepath.Dir(logPath) | ||
| 79 | if _, err := os.Stat(dir); os.IsNotExist(err) { | 93 | if _, err := os.Stat(dir); os.IsNotExist(err) { | ||
| 80 | if mkdirErr := os.MkdirAll(dir, 0755); mkdirErr != nil { | 94 | if mkdirErr := os.MkdirAll(dir, 0755); mkdirErr != nil { | ||
| @@ -90,7 +104,7 @@ func initializeLoggerOutput(logFile string) io.Writer { | |||||
| 90 | return zerolog.MultiLevelWriter(file, consoleWriter) | 104 | return zerolog.MultiLevelWriter(file, consoleWriter) | ||
| 91 | } | 105 | } | ||
| 92 | 106 | ||||
| 93 | - | func SetupLogging() { | 107 | + | func SetupLogging(cfg *Config) { |
| 94 | err := utils.CreateLogsDir(cfg.LogDir) | 108 | err := utils.CreateLogsDir(cfg.LogDir) | ||
| 95 | if err != nil { | 109 | if err != nil { | ||
| 96 | panic(err) | 110 | panic(err) | ||
M internal/http/handlers/errors.go +5 -7
| @@ -5,20 +5,18 @@ import ( | |||
| 5 | 5 | "net/http" | |
| 6 | 6 | ||
| 7 | 7 | "github.com/labstack/echo/v4" | |
| 8 | - | "gogs.bnema.dev/gordon-echo/internal/ui" | |
| 8 | + | "gogs.bnema.dev/gordon-echo/internal/app" | |
| 9 | 9 | "gogs.bnema.dev/gordon-echo/pkg/utils" | |
| 10 | 10 | ) | |
| 11 | 11 | ||
| 12 | - | type ErrorHandler struct { | |
| 13 | - | Err error | |
| 14 | - | C echo.Context | |
| 15 | - | ErrorNumber int | |
| 12 | + | type AppConfig struct { | |
| 13 | + | *app.Config | |
| 16 | 14 | } | |
| 17 | 15 | ||
| 18 | - | func Error404Handler(err error, c echo.Context) { | |
| 16 | + | func (a *AppConfig) Error404Handler(err error, c echo.Context) { | |
| 19 | 17 | if he, ok := err.(*echo.HTTPError); ok { | |
| 20 | 18 | if he.Code == http.StatusNotFound { | |
| 21 | - | renderer, err := utils.GetRenderer("404.gohtml", ui.PublicFS, utils.NewLogger()) | |
| 19 | + | renderer, err := utils.GetRenderer("404.gohtml", a.Config.PublicFS, utils.NewLogger()) | |
| 22 | 20 | if err != nil { | |
| 23 | 21 | c.String(http.StatusInternalServerError, fmt.Sprintf("%v", err)) | |
| 24 | 22 | } | |
| @@ -5,20 +5,18 @@ 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/ui" | 8 | + | "gogs.bnema.dev/gordon-echo/internal/app" |
| 9 | "gogs.bnema.dev/gordon-echo/pkg/utils" | 9 | "gogs.bnema.dev/gordon-echo/pkg/utils" | ||
| 10 | ) | 10 | ) | ||
| 11 | 11 | ||||
| 12 | - | type ErrorHandler struct { | 12 | + | type AppConfig struct { |
| 13 | - | Err error | 13 | + | *app.Config |
| 14 | - | C echo.Context | |||
| 15 | - | ErrorNumber int | |||
| 16 | } | 14 | } | ||
| 17 | 15 | ||||
| 18 | - | func Error404Handler(err error, c echo.Context) { | 16 | + | func (a *AppConfig) Error404Handler(err error, c echo.Context) { |
| 19 | if he, ok := err.(*echo.HTTPError); ok { | 17 | if he, ok := err.(*echo.HTTPError); ok { | ||
| 20 | if he.Code == http.StatusNotFound { | 18 | if he.Code == http.StatusNotFound { | ||
| 21 | - | renderer, err := utils.GetRenderer("404.gohtml", ui.PublicFS, utils.NewLogger()) | 19 | + | renderer, err := utils.GetRenderer("404.gohtml", a.Config.PublicFS, utils.NewLogger()) |
| 22 | if err != nil { | 20 | if err != nil { | ||
| 23 | c.String(http.StatusInternalServerError, fmt.Sprintf("%v", err)) | 21 | c.String(http.StatusInternalServerError, fmt.Sprintf("%v", err)) | ||
| 24 | } | 22 | } | ||
M internal/http/handlers/htmx-installer.go +18 -6
| @@ -4,14 +4,19 @@ import ( | |||
| 4 | 4 | "fmt" | |
| 5 | 5 | ||
| 6 | 6 | "github.com/labstack/echo/v4" | |
| 7 | + | "gogs.bnema.dev/gordon-echo/pkg/scripts" | |
| 7 | 8 | "gogs.bnema.dev/gordon-echo/pkg/utils" | |
| 8 | 9 | ) | |
| 9 | 10 | ||
| 10 | - | // Create a RejectPOSTPolicy function to reject POST requests if they are not allowed | |
| 11 | - | ||
| 12 | - | func traefikInstallerHandler(c echo.Context, logger *utils.Logger) error { | |
| 11 | + | // TraefikInstallerHandler handles the installation of Traefik | |
| 12 | + | func TraefikInstallerHandler(c echo.Context) error { | |
| 13 | + | // Retrieve the logger from the context | |
| 14 | + | logger := c.Get("logger").(*utils.Logger) | |
| 13 | 15 | // Reject POST requests if they are not allowed with the RejectPOSTPolicy function | |
| 14 | - | RejectPOSTPolicy(c) | |
| 16 | + | if err := RejectPOSTPolicy(c); err != nil { | |
| 17 | + | logger.Error().Err(err).Msg("POST request rejected") | |
| 18 | + | return err | |
| 19 | + | } | |
| 15 | 20 | ||
| 16 | 21 | // Get data from the form install-traefik-form (input fields : topdomain, adminemail) and sanitize it | |
| 17 | 22 | topDomain, err := SanitizeUserInput(c.FormValue("topdomain")) | |
| @@ -25,9 +30,16 @@ func traefikInstallerHandler(c echo.Context, logger *utils.Logger) error { | |||
| 25 | 30 | return err | |
| 26 | 31 | } | |
| 27 | 32 | ||
| 28 | - | fmt.Println(topDomain, adminEmail) | |
| 33 | + | fmt.Println("POST request PAYLOAD=", topDomain, adminEmail) | |
| 29 | 34 | ||
| 30 | - | // stop here for now | |
| 35 | + | // Call to create the YAML files | |
| 36 | + | err = scripts.CreateYAMLFiles(topDomain, adminEmail, logger) | |
| 37 | + | if err != nil { | |
| 38 | + | logger.Error().Err(err).Msg("Failed to create YAML files") | |
| 39 | + | return err | |
| 40 | + | } | |
| 31 | 41 | return nil | |
| 32 | 42 | ||
| 33 | 43 | } | |
| 44 | + | ||
| 45 | + | // use the handler at the route /admin/install/traefik | |
| @@ -4,14 +4,19 @@ import ( | |||||
| 4 | "fmt" | 4 | "fmt" | ||
| 5 | 5 | ||||
| 6 | "github.com/labstack/echo/v4" | 6 | "github.com/labstack/echo/v4" | ||
| 7 | + | "gogs.bnema.dev/gordon-echo/pkg/scripts" | |||
| 7 | "gogs.bnema.dev/gordon-echo/pkg/utils" | 8 | "gogs.bnema.dev/gordon-echo/pkg/utils" | ||
| 8 | ) | 9 | ) | ||
| 9 | 10 | ||||
| 10 | - | // Create a RejectPOSTPolicy function to reject POST requests if they are not allowed | 11 | + | // TraefikInstallerHandler handles the installation of Traefik |
| 11 | - | 12 | + | func TraefikInstallerHandler(c echo.Context) error { | |
| 12 | - | func traefikInstallerHandler(c echo.Context, logger *utils.Logger) error { | 13 | + | // Retrieve the logger from the context |
| 14 | + | logger := c.Get("logger").(*utils.Logger) | |||
| 13 | // Reject POST requests if they are not allowed with the RejectPOSTPolicy function | 15 | // Reject POST requests if they are not allowed with the RejectPOSTPolicy function | ||
| 14 | - | RejectPOSTPolicy(c) | 16 | + | if err := RejectPOSTPolicy(c); err != nil { |
| 17 | + | logger.Error().Err(err).Msg("POST request rejected") | |||
| 18 | + | return err | |||
| 19 | + | } | |||
| 15 | 20 | ||||
| 16 | // Get data from the form install-traefik-form (input fields : topdomain, adminemail) and sanitize it | 21 | // Get data from the form install-traefik-form (input fields : topdomain, adminemail) and sanitize it | ||
| 17 | topDomain, err := SanitizeUserInput(c.FormValue("topdomain")) | 22 | topDomain, err := SanitizeUserInput(c.FormValue("topdomain")) | ||
| @@ -25,9 +30,16 @@ func traefikInstallerHandler(c echo.Context, logger *utils.Logger) error { | |||||
| 25 | return err | 30 | return err | ||
| 26 | } | 31 | } | ||
| 27 | 32 | ||||
| 28 | - | fmt.Println(topDomain, adminEmail) | 33 | + | fmt.Println("POST request PAYLOAD=", topDomain, adminEmail) |
| 29 | 34 | ||||
| 30 | - | // stop here for now | 35 | + | // Call to create the YAML files |
| 36 | + | err = scripts.CreateYAMLFiles(topDomain, adminEmail, logger) | |||
| 37 | + | if err != nil { | |||
| 38 | + | logger.Error().Err(err).Msg("Failed to create YAML files") | |||
| 39 | + | return err | |||
| 40 | + | } | |||
| 31 | return nil | 41 | return nil | ||
| 32 | 42 | ||||
| 33 | } | 43 | } | ||
| 44 | + | ||||
| 45 | + | // use the handler at the route /admin/install/traefik | |||
M internal/http/handlers/post.go +0 -5
| @@ -25,11 +25,6 @@ func RejectPOSTPolicy(c echo.Context) error { | |||
| 25 | 25 | return fmt.Errorf("missing user agent") | |
| 26 | 26 | } | |
| 27 | 27 | ||
| 28 | - | // Check for suspicious content types, e.g., "application/x-www-form-urlencoded" | |
| 29 | - | if c.Request().Header.Get("Content-Type") != "application/json" { | |
| 30 | - | return fmt.Errorf("invalid content type") | |
| 31 | - | } | |
| 32 | - | ||
| 33 | 28 | // If all checks pass | |
| 34 | 29 | return nil | |
| 35 | 30 | } | |
| @@ -25,11 +25,6 @@ func RejectPOSTPolicy(c echo.Context) error { | |||||
| 25 | return fmt.Errorf("missing user agent") | 25 | return fmt.Errorf("missing user agent") | ||
| 26 | } | 26 | } | ||
| 27 | 27 | ||||
| 28 | - | // Check for suspicious content types, e.g., "application/x-www-form-urlencoded" | |||
| 29 | - | if c.Request().Header.Get("Content-Type") != "application/json" { | |||
| 30 | - | return fmt.Errorf("invalid content type") | |||
| 31 | - | } | |||
| 32 | - | ||||
| 33 | // If all checks pass | 28 | // If all checks pass | ||
| 34 | return nil | 29 | return nil | ||
| 35 | } | 30 | } | ||
M internal/http/middlewares/logger.go +18 -2
| @@ -1,16 +1,23 @@ | |||
| 1 | 1 | package middlewares | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | + | "strings" | |
| 5 | + | ||
| 4 | 6 | "github.com/labstack/echo/v4" | |
| 5 | 7 | "github.com/labstack/echo/v4/middleware" | |
| 6 | - | "github.com/rs/zerolog" | |
| 8 | + | "gogs.bnema.dev/gordon-echo/pkg/utils" | |
| 7 | 9 | ) | |
| 8 | 10 | ||
| 9 | - | func NewRequestLogger(logger zerolog.Logger) echo.MiddlewareFunc { | |
| 11 | + | // HTTPAccessLogger creates a new middleware to log HTTP requests using the provided HttpLogger. | |
| 12 | + | func HTTPAccessLogger(logger *utils.Logger) echo.MiddlewareFunc { | |
| 10 | 13 | return middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ | |
| 11 | 14 | LogURI: true, | |
| 12 | 15 | LogStatus: true, | |
| 13 | 16 | LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error { | |
| 17 | + | // Filter out any requests that contain the substring "sse" in the URI | |
| 18 | + | if strings.Contains(v.URI, "sse") { | |
| 19 | + | return nil | |
| 20 | + | } | |
| 14 | 21 | logger.Info(). | |
| 15 | 22 | Str("type", "http"). | |
| 16 | 23 | Str("remote_ip", c.RealIP()). | |
| @@ -22,3 +29,12 @@ func NewRequestLogger(logger zerolog.Logger) echo.MiddlewareFunc { | |||
| 22 | 29 | }, | |
| 23 | 30 | }) | |
| 24 | 31 | } | |
| 32 | + | ||
| 33 | + | func SetLogger(logger *utils.Logger) echo.MiddlewareFunc { | |
| 34 | + | return func(next echo.HandlerFunc) echo.HandlerFunc { | |
| 35 | + | return func(c echo.Context) error { | |
| 36 | + | c.Set("logger", logger) | |
| 37 | + | return next(c) | |
| 38 | + | } | |
| 39 | + | } | |
| 40 | + | } | |
| @@ -1,16 +1,23 @@ | |||||
| 1 | package middlewares | 1 | package middlewares | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | + | "strings" | |||
| 5 | + | ||||
| 4 | "github.com/labstack/echo/v4" | 6 | "github.com/labstack/echo/v4" | ||
| 5 | "github.com/labstack/echo/v4/middleware" | 7 | "github.com/labstack/echo/v4/middleware" | ||
| 6 | - | "github.com/rs/zerolog" | 8 | + | "gogs.bnema.dev/gordon-echo/pkg/utils" |
| 7 | ) | 9 | ) | ||
| 8 | 10 | ||||
| 9 | - | func NewRequestLogger(logger zerolog.Logger) echo.MiddlewareFunc { | 11 | + | // HTTPAccessLogger creates a new middleware to log HTTP requests using the provided HttpLogger. |
| 12 | + | func HTTPAccessLogger(logger *utils.Logger) echo.MiddlewareFunc { | |||
| 10 | return middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ | 13 | return middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ | ||
| 11 | LogURI: true, | 14 | LogURI: true, | ||
| 12 | LogStatus: true, | 15 | LogStatus: true, | ||
| 13 | LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error { | 16 | LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error { | ||
| 17 | + | // Filter out any requests that contain the substring "sse" in the URI | |||
| 18 | + | if strings.Contains(v.URI, "sse") { | |||
| 19 | + | return nil | |||
| 20 | + | } | |||
| 14 | logger.Info(). | 21 | logger.Info(). | ||
| 15 | Str("type", "http"). | 22 | Str("type", "http"). | ||
| 16 | Str("remote_ip", c.RealIP()). | 23 | Str("remote_ip", c.RealIP()). | ||
| @@ -22,3 +29,12 @@ func NewRequestLogger(logger zerolog.Logger) echo.MiddlewareFunc { | |||||
| 22 | }, | 29 | }, | ||
| 23 | }) | 30 | }) | ||
| 24 | } | 31 | } | ||
| 32 | + | ||||
| 33 | + | func SetLogger(logger *utils.Logger) echo.MiddlewareFunc { | |||
| 34 | + | return func(next echo.HandlerFunc) echo.HandlerFunc { | |||
| 35 | + | return func(c echo.Context) error { | |||
| 36 | + | c.Set("logger", logger) | |||
| 37 | + | return next(c) | |||
| 38 | + | } | |||
| 39 | + | } | |||
| 40 | + | } | |||
M internal/http/routes/admin-routes.go +11 -7
| @@ -5,12 +5,16 @@ import ( | |||
| 5 | 5 | "net/http" | |
| 6 | 6 | ||
| 7 | 7 | "github.com/labstack/echo/v4" | |
| 8 | + | "gogs.bnema.dev/gordon-echo/config" | |
| 8 | 9 | "gogs.bnema.dev/gordon-echo/internal/http/middlewares" | |
| 9 | - | "gogs.bnema.dev/gordon-echo/internal/ui" | |
| 10 | 10 | "gogs.bnema.dev/gordon-echo/pkg/utils" | |
| 11 | 11 | ) | |
| 12 | 12 | ||
| 13 | - | func AdminRoute(c echo.Context) error { | |
| 13 | + | type AppConfig struct { | |
| 14 | + | Config config.Provider | |
| 15 | + | } | |
| 16 | + | ||
| 17 | + | func (a *AppConfig) AdminRoute(c echo.Context) error { | |
| 14 | 18 | // Check if it's the first launch of the app | |
| 15 | 19 | if utils.IsConfigFilePresent() { | |
| 16 | 20 | fmt.Println("Fresh installation detected, redirecting to /admin/install") | |
| @@ -20,12 +24,12 @@ func AdminRoute(c echo.Context) error { | |||
| 20 | 24 | // Retrieve the current language from the context | |
| 21 | 25 | lang := c.Get(middlewares.LangKey).(string) | |
| 22 | 26 | ||
| 23 | - | staticData, err := utils.PopulateDataFromYAML(lang) | |
| 27 | + | staticData, err := a.PopulateDataFromYAML(lang) | |
| 24 | 28 | if err != nil { | |
| 25 | 29 | return err | |
| 26 | 30 | } | |
| 27 | 31 | ||
| 28 | - | renderer, err := utils.GetRenderer("index.gohtml", ui.PublicFS, utils.NewLogger()) | |
| 32 | + | renderer, err := utils.GetRenderer("index.gohtml", a.Config.GetPublicFS(), utils.NewLogger()) | |
| 29 | 33 | if err != nil { | |
| 30 | 34 | return err | |
| 31 | 35 | } | |
| @@ -40,7 +44,7 @@ func AdminRoute(c echo.Context) error { | |||
| 40 | 44 | return c.HTML(http.StatusOK, html) | |
| 41 | 45 | } | |
| 42 | 46 | ||
| 43 | - | func InstallRoute(c echo.Context) error { | |
| 47 | + | func (a *AppConfig) InstallRoute(c echo.Context) error { | |
| 44 | 48 | // Check if it's the first launch of the app | |
| 45 | 49 | if !utils.IsConfigFilePresent() { | |
| 46 | 50 | fmt.Println("Config file already present, redirecting to /admin") | |
| @@ -50,12 +54,12 @@ func InstallRoute(c echo.Context) error { | |||
| 50 | 54 | // Retrieve the current language from the context | |
| 51 | 55 | lang := c.Get(middlewares.LangKey).(string) | |
| 52 | 56 | ||
| 53 | - | staticData, err := utils.PopulateDataFromYAML(lang) | |
| 57 | + | staticData, err := a.PopulateDataFromYAML(lang) | |
| 54 | 58 | if err != nil { | |
| 55 | 59 | return err | |
| 56 | 60 | } | |
| 57 | 61 | ||
| 58 | - | renderer, err := utils.GetRenderer("install.gohtml", ui.TemplateFS, utils.NewLogger()) | |
| 62 | + | renderer, err := utils.GetRenderer("install.gohtml", a.Config.GetTemplateFS(), utils.NewLogger()) | |
| 59 | 63 | if err != nil { | |
| 60 | 64 | return err | |
| 61 | 65 | } | |
| @@ -5,12 +5,16 @@ 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/config" | |||
| 8 | "gogs.bnema.dev/gordon-echo/internal/http/middlewares" | 9 | "gogs.bnema.dev/gordon-echo/internal/http/middlewares" | ||
| 9 | - | "gogs.bnema.dev/gordon-echo/internal/ui" | |||
| 10 | "gogs.bnema.dev/gordon-echo/pkg/utils" | 10 | "gogs.bnema.dev/gordon-echo/pkg/utils" | ||
| 11 | ) | 11 | ) | ||
| 12 | 12 | ||||
| 13 | - | func AdminRoute(c echo.Context) error { | 13 | + | type AppConfig struct { |
| 14 | + | Config config.Provider | |||
| 15 | + | } | |||
| 16 | + | ||||
| 17 | + | func (a *AppConfig) AdminRoute(c echo.Context) error { | |||
| 14 | // Check if it's the first launch of the app | 18 | // Check if it's the first launch of the app | ||
| 15 | if utils.IsConfigFilePresent() { | 19 | if utils.IsConfigFilePresent() { | ||
| 16 | fmt.Println("Fresh installation detected, redirecting to /admin/install") | 20 | fmt.Println("Fresh installation detected, redirecting to /admin/install") | ||
| @@ -20,12 +24,12 @@ func AdminRoute(c echo.Context) error { | |||||
| 20 | // Retrieve the current language from the context | 24 | // Retrieve the current language from the context | ||
| 21 | lang := c.Get(middlewares.LangKey).(string) | 25 | lang := c.Get(middlewares.LangKey).(string) | ||
| 22 | 26 | ||||
| 23 | - | staticData, err := utils.PopulateDataFromYAML(lang) | 27 | + | staticData, err := a.PopulateDataFromYAML(lang) |
| 24 | if err != nil { | 28 | if err != nil { | ||
| 25 | return err | 29 | return err | ||
| 26 | } | 30 | } | ||
| 27 | 31 | ||||
| 28 | - | renderer, err := utils.GetRenderer("index.gohtml", ui.PublicFS, utils.NewLogger()) | 32 | + | renderer, err := utils.GetRenderer("index.gohtml", a.Config.GetPublicFS(), utils.NewLogger()) |
| 29 | if err != nil { | 33 | if err != nil { | ||
| 30 | return err | 34 | return err | ||
| 31 | } | 35 | } | ||
| @@ -40,7 +44,7 @@ func AdminRoute(c echo.Context) error { | |||||
| 40 | return c.HTML(http.StatusOK, html) | 44 | return c.HTML(http.StatusOK, html) | ||
| 41 | } | 45 | } | ||
| 42 | 46 | ||||
| 43 | - | func InstallRoute(c echo.Context) error { | 47 | + | func (a *AppConfig) InstallRoute(c echo.Context) error { |
| 44 | // Check if it's the first launch of the app | 48 | // Check if it's the first launch of the app | ||
| 45 | if !utils.IsConfigFilePresent() { | 49 | if !utils.IsConfigFilePresent() { | ||
| 46 | fmt.Println("Config file already present, redirecting to /admin") | 50 | fmt.Println("Config file already present, redirecting to /admin") | ||
| @@ -50,12 +54,12 @@ func InstallRoute(c echo.Context) error { | |||||
| 50 | // Retrieve the current language from the context | 54 | // Retrieve the current language from the context | ||
| 51 | lang := c.Get(middlewares.LangKey).(string) | 55 | lang := c.Get(middlewares.LangKey).(string) | ||
| 52 | 56 | ||||
| 53 | - | staticData, err := utils.PopulateDataFromYAML(lang) | 57 | + | staticData, err := a.PopulateDataFromYAML(lang) |
| 54 | if err != nil { | 58 | if err != nil { | ||
| 55 | return err | 59 | return err | ||
| 56 | } | 60 | } | ||
| 57 | 61 | ||||
| 58 | - | renderer, err := utils.GetRenderer("install.gohtml", ui.TemplateFS, utils.NewLogger()) | 62 | + | renderer, err := utils.GetRenderer("install.gohtml", a.Config.GetTemplateFS(), utils.NewLogger()) |
| 59 | if err != nil { | 63 | if err != nil { | ||
| 60 | return err | 64 | return err | ||
| 61 | } | 65 | } | ||
M internal/http/routes/router.go +23 -9
| @@ -8,23 +8,37 @@ import ( | |||
| 8 | 8 | ) | |
| 9 | 9 | ||
| 10 | 10 | func ConfigureRouter(e *echo.Echo, app *app.App) *echo.Echo { | |
| 11 | - | // The logger is already set in main.go, so we don't need to set it again here. | |
| 12 | - | ||
| 13 | - | // Register middlewares | |
| 14 | - | e.Use(middlewares.NewRequestLogger(app.HttpLogger.Logger)) | |
| 11 | + | // SetLogger is a middleware that sets the logger in the context | |
| 12 | + | e.Use(middlewares.SetLogger(app.HttpLogger)) | |
| 13 | + | // HTTPAccessLogger is a middleware that logs HTTP requests | |
| 14 | + | e.Use(middlewares.HTTPAccessLogger(app.HttpLogger)) | |
| 15 | 15 | e.Use(middlewares.LanguageDetection) | |
| 16 | 16 | e.Use(middlewares.ColorSchemeDetection) | |
| 17 | - | //e.Use(middlewares.SecureMiddleware()) // Uncomment this line to enable the secure middleware | |
| 18 | - | // Register routes | |
| 19 | - | e = bindStaticAdminUI(e) | |
| 17 | + | //e.Use(middlewares.SecureMiddleware()) // Need testing | |
| 20 | 18 | ||
| 19 | + | // Register routes | |
| 20 | + | e = bindAdminRoutes(e) | |
| 21 | + | e = bindHtmxRoutes(e) | |
| 22 | + | e = bindStaticRoutes(e) | |
| 23 | + | e.HTTPErrorHandler = handlers.ErrorNumberHandler | |
| 21 | 24 | return e | |
| 22 | 25 | } | |
| 23 | - | func bindStaticAdminUI(e *echo.Echo) *echo.Echo { | |
| 26 | + | func bindAdminRoutes(e *echo.Echo) *echo.Echo { | |
| 24 | 27 | e.GET("/admin", AdminRoute) | |
| 25 | 28 | e.GET("/admin/install", InstallRoute) | |
| 29 | + | e.POST("/admin/install/traefik", handlers.TraefikInstallerHandler) | |
| 30 | + | return e | |
| 31 | + | } | |
| 32 | + | ||
| 33 | + | func bindHtmxRoutes(e *echo.Echo) *echo.Echo { | |
| 26 | 34 | e.GET("/htmx", handlers.HTMXHandler) | |
| 35 | + | e.GET("/htmx/fragment", handlers.HTMXHandler) | |
| 36 | + | e.POST("/htmx", handlers.HTMXHandler) | |
| 37 | + | e.POST("/htmx/fragment", handlers.HTMXHandler) | |
| 38 | + | return e | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | func bindStaticRoutes(e *echo.Echo) *echo.Echo { | |
| 27 | 42 | e.GET("/*", StaticRoute) | |
| 28 | - | e.HTTPErrorHandler = handlers.ErrorNumberHandler | |
| 29 | 43 | return e | |
| 30 | 44 | } | |
| @@ -8,23 +8,37 @@ import ( | |||||
| 8 | ) | 8 | ) | ||
| 9 | 9 | ||||
| 10 | func ConfigureRouter(e *echo.Echo, app *app.App) *echo.Echo { | 10 | func ConfigureRouter(e *echo.Echo, app *app.App) *echo.Echo { | ||
| 11 | - | // The logger is already set in main.go, so we don't need to set it again here. | 11 | + | // SetLogger is a middleware that sets the logger in the context |
| 12 | - | 12 | + | e.Use(middlewares.SetLogger(app.HttpLogger)) | |
| 13 | - | // Register middlewares | 13 | + | // HTTPAccessLogger is a middleware that logs HTTP requests |
| 14 | - | e.Use(middlewares.NewRequestLogger(app.HttpLogger.Logger)) | 14 | + | e.Use(middlewares.HTTPAccessLogger(app.HttpLogger)) |
| 15 | e.Use(middlewares.LanguageDetection) | 15 | e.Use(middlewares.LanguageDetection) | ||
| 16 | e.Use(middlewares.ColorSchemeDetection) | 16 | e.Use(middlewares.ColorSchemeDetection) | ||
| 17 | - | //e.Use(middlewares.SecureMiddleware()) // Uncomment this line to enable the secure middleware | 17 | + | //e.Use(middlewares.SecureMiddleware()) // Need testing |
| 18 | - | // Register routes | |||
| 19 | - | e = bindStaticAdminUI(e) | |||
| 20 | 18 | ||||
| 19 | + | // Register routes | |||
| 20 | + | e = bindAdminRoutes(e) | |||
| 21 | + | e = bindHtmxRoutes(e) | |||
| 22 | + | e = bindStaticRoutes(e) | |||
| 23 | + | e.HTTPErrorHandler = handlers.ErrorNumberHandler | |||
| 21 | return e | 24 | return e | ||
| 22 | } | 25 | } | ||
| 23 | - | func bindStaticAdminUI(e *echo.Echo) *echo.Echo { | 26 | + | func bindAdminRoutes(e *echo.Echo) *echo.Echo { |
| 24 | e.GET("/admin", AdminRoute) | 27 | e.GET("/admin", AdminRoute) | ||
| 25 | e.GET("/admin/install", InstallRoute) | 28 | e.GET("/admin/install", InstallRoute) | ||
| 29 | + | e.POST("/admin/install/traefik", handlers.TraefikInstallerHandler) | |||
| 30 | + | return e | |||
| 31 | + | } | |||
| 32 | + | ||||
| 33 | + | func bindHtmxRoutes(e *echo.Echo) *echo.Echo { | |||
| 26 | e.GET("/htmx", handlers.HTMXHandler) | 34 | e.GET("/htmx", handlers.HTMXHandler) | ||
| 35 | + | e.GET("/htmx/fragment", handlers.HTMXHandler) | |||
| 36 | + | e.POST("/htmx", handlers.HTMXHandler) | |||
| 37 | + | e.POST("/htmx/fragment", handlers.HTMXHandler) | |||
| 38 | + | return e | |||
| 39 | + | } | |||
| 40 | + | ||||
| 41 | + | func bindStaticRoutes(e *echo.Echo) *echo.Echo { | |||
| 27 | e.GET("/*", StaticRoute) | 42 | e.GET("/*", StaticRoute) | ||
| 28 | - | e.HTTPErrorHandler = handlers.ErrorNumberHandler | |||
| 29 | return e | 43 | return e | ||
| 30 | } | 44 | } | ||
M internal/http/routes/static-routes.go +2 -3
| @@ -4,10 +4,9 @@ import ( | |||
| 4 | 4 | "os" | |
| 5 | 5 | ||
| 6 | 6 | "github.com/labstack/echo/v4" | |
| 7 | - | "gogs.bnema.dev/gordon-echo/internal/ui" | |
| 8 | 7 | ) | |
| 9 | 8 | ||
| 10 | - | func StaticRoute(c echo.Context) error { | |
| 9 | + | func (a *AppConfig) StaticRoute(c echo.Context) error { | |
| 11 | 10 | ||
| 12 | 11 | // Set the cache-control header to cache the static files for 1 day if PROD env is set to true | |
| 13 | 12 | ||
| @@ -16,5 +15,5 @@ func StaticRoute(c echo.Context) error { | |||
| 16 | 15 | } else { | |
| 17 | 16 | c.Response().Header().Set("Cache-Control", "no-cache") | |
| 18 | 17 | } | |
| 19 | - | return echo.StaticDirectoryHandler(ui.PublicFS, false)(c) | |
| 18 | + | return echo.StaticDirectoryHandler(a.Config.GetPublicFS(), false)(c) | |
| 20 | 19 | } | |
| @@ -4,10 +4,9 @@ import ( | |||||
| 4 | "os" | 4 | "os" | ||
| 5 | 5 | ||||
| 6 | "github.com/labstack/echo/v4" | 6 | "github.com/labstack/echo/v4" | ||
| 7 | - | "gogs.bnema.dev/gordon-echo/internal/ui" | |||
| 8 | ) | 7 | ) | ||
| 9 | 8 | ||||
| 10 | - | func StaticRoute(c echo.Context) error { | 9 | + | func (a *AppConfig) StaticRoute(c echo.Context) error { |
| 11 | 10 | ||||
| 12 | // Set the cache-control header to cache the static files for 1 day if PROD env is set to true | 11 | // Set the cache-control header to cache the static files for 1 day if PROD env is set to true | ||
| 13 | 12 | ||||
| @@ -16,5 +15,5 @@ func StaticRoute(c echo.Context) error { | |||||
| 16 | } else { | 15 | } else { | ||
| 17 | c.Response().Header().Set("Cache-Control", "no-cache") | 16 | c.Response().Header().Set("Cache-Control", "no-cache") | ||
| 18 | } | 17 | } | ||
| 19 | - | return echo.StaticDirectoryHandler(ui.PublicFS, false)(c) | 18 | + | return echo.StaticDirectoryHandler(a.Config.GetPublicFS(), false)(c) |
| 20 | } | 19 | } | ||
D internal/ui/embed.go +0 -17
| @@ -1,17 +0,0 @@ | |||
| 1 | - | package ui | |
| 2 | - | ||
| 3 | - | import ( | |
| 4 | - | "embed" | |
| 5 | - | ||
| 6 | - | "github.com/labstack/echo/v4" | |
| 7 | - | ) | |
| 8 | - | ||
| 9 | - | //go:embed public/* | |
| 10 | - | var public embed.FS | |
| 11 | - | ||
| 12 | - | //go:embed templates/* | |
| 13 | - | var templates embed.FS | |
| 14 | - | ||
| 15 | - | var PublicFS = echo.MustSubFS(public, "public") | |
| 16 | - | ||
| 17 | - | var TemplateFS = echo.MustSubFS(templates, "templates") | |
| @@ -1,17 +0,0 @@ | |||||
| 1 | - | package ui | |||
| 2 | - | ||||
| 3 | - | import ( | |||
| 4 | - | "embed" | |||
| 5 | - | ||||
| 6 | - | "github.com/labstack/echo/v4" | |||
| 7 | - | ) | |||
| 8 | - | ||||
| 9 | - | //go:embed public/* | |||
| 10 | - | var public embed.FS | |||
| 11 | - | ||||
| 12 | - | //go:embed templates/* | |||
| 13 | - | var templates embed.FS | |||
| 14 | - | ||||
| 15 | - | var PublicFS = echo.MustSubFS(public, "public") | |||
| 16 | - | ||||
| 17 | - | var TemplateFS = echo.MustSubFS(templates, "templates") | |||
M internal/ui/templates/install.gohtml +3 -2
| @@ -59,9 +59,10 @@ | |||
| 59 | 59 | <div class="w-full md:w-1/2 p-4"> | |
| 60 | 60 | <form | |
| 61 | 61 | id="install-traefik-form" | |
| 62 | - | action="/install/traefik" | |
| 62 | + | action="/admin/install/traefik" | |
| 63 | 63 | method="POST" | |
| 64 | - | hx-post="/install/traefik" | |
| 64 | + | hx-post="/admin/install/traefik" | |
| 65 | + | hx-swap="outerHTML" | |
| 65 | 66 | > | |
| 66 | 67 | <h2 class="">1.1 - Set your top domain name</h2> | |
| 67 | 68 | <input | |
| @@ -59,9 +59,10 @@ | |||||
| 59 | <div class="w-full md:w-1/2 p-4"> | 59 | <div class="w-full md:w-1/2 p-4"> | ||
| 60 | <form | 60 | <form | ||
| 61 | id="install-traefik-form" | 61 | id="install-traefik-form" | ||
| 62 | - | action="/install/traefik" | 62 | + | action="/admin/install/traefik" |
| 63 | method="POST" | 63 | method="POST" | ||
| 64 | - | hx-post="/install/traefik" | 64 | + | hx-post="/admin/install/traefik" |
| 65 | + | hx-swap="outerHTML" | |||
| 65 | > | 66 | > | ||
| 66 | <h2 class="">1.1 - Set your top domain name</h2> | 67 | <h2 class="">1.1 - Set your top domain name</h2> | ||
| 67 | <input | 68 | <input | ||
M pkg/docker/registry.go +13 -0
| @@ -5,12 +5,14 @@ import ( | |||
| 5 | 5 | "fmt" | |
| 6 | 6 | "io" | |
| 7 | 7 | "os" | |
| 8 | + | "os/exec" | |
| 8 | 9 | ||
| 9 | 10 | "github.com/docker/docker/api/types" | |
| 10 | 11 | "github.com/docker/docker/api/types/container" | |
| 11 | 12 | "github.com/docker/docker/client" | |
| 12 | 13 | "github.com/docker/docker/pkg/archive" | |
| 13 | 14 | "github.com/docker/docker/pkg/stdcopy" | |
| 15 | + | "gogs.bnema.dev/gordon-echo/pkg/utils" | |
| 14 | 16 | ) | |
| 15 | 17 | ||
| 16 | 18 | func DockertPullImageTest() { | |
| @@ -125,3 +127,14 @@ func CreateImageFromDockerfile() { | |||
| 125 | 127 | panic(err) | |
| 126 | 128 | } | |
| 127 | 129 | } | |
| 130 | + | ||
| 131 | + | func RunCommand(command string, arg string, logger *utils.Logger) error { | |
| 132 | + | cmd := exec.Command(command, arg) | |
| 133 | + | cmd.Stdout = logger | |
| 134 | + | cmd.Stderr = logger | |
| 135 | + | err := cmd.Run() | |
| 136 | + | if err != nil { | |
| 137 | + | return err | |
| 138 | + | } | |
| 139 | + | return nil | |
| 140 | + | } | |
| @@ -5,12 +5,14 @@ import ( | |||||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | "io" | 6 | "io" | ||
| 7 | "os" | 7 | "os" | ||
| 8 | + | "os/exec" | |||
| 8 | 9 | ||||
| 9 | "github.com/docker/docker/api/types" | 10 | "github.com/docker/docker/api/types" | ||
| 10 | "github.com/docker/docker/api/types/container" | 11 | "github.com/docker/docker/api/types/container" | ||
| 11 | "github.com/docker/docker/client" | 12 | "github.com/docker/docker/client" | ||
| 12 | "github.com/docker/docker/pkg/archive" | 13 | "github.com/docker/docker/pkg/archive" | ||
| 13 | "github.com/docker/docker/pkg/stdcopy" | 14 | "github.com/docker/docker/pkg/stdcopy" | ||
| 15 | + | "gogs.bnema.dev/gordon-echo/pkg/utils" | |||
| 14 | ) | 16 | ) | ||
| 15 | 17 | ||||
| 16 | func DockertPullImageTest() { | 18 | func DockertPullImageTest() { | ||
| @@ -125,3 +127,14 @@ func CreateImageFromDockerfile() { | |||||
| 125 | panic(err) | 127 | panic(err) | ||
| 126 | } | 128 | } | ||
| 127 | } | 129 | } | ||
| 130 | + | ||||
| 131 | + | func RunCommand(command string, arg string, logger *utils.Logger) error { | |||
| 132 | + | cmd := exec.Command(command, arg) | |||
| 133 | + | cmd.Stdout = logger | |||
| 134 | + | cmd.Stderr = logger | |||
| 135 | + | err := cmd.Run() | |||
| 136 | + | if err != nil { | |||
| 137 | + | return err | |||
| 138 | + | } | |||
| 139 | + | return nil | |||
| 140 | + | } | |||
M pkg/scripts/install-traefik.go +51 -0
| @@ -1,1 +1,52 @@ | |||
| 1 | 1 | package scripts | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "os" | |
| 5 | + | ||
| 6 | + | "gogs.bnema.dev/gordon-echo/pkg/templating" | |
| 7 | + | "gogs.bnema.dev/gordon-echo/pkg/utils" | |
| 8 | + | ) | |
| 9 | + | ||
| 10 | + | func CreateYAMLFiles(topDomain, adminEmail string, logger *utils.Logger) error { | |
| 11 | + | // Setup the TXT renderer for traefik-compose.goyml | |
| 12 | + | rendererCompose, err := templating.GetTXTRenderer("traefik-compose.goyml", nil, logger) | |
| 13 | + | if err != nil { | |
| 14 | + | return err | |
| 15 | + | } | |
| 16 | + | ||
| 17 | + | // Render the traefik-compose.goyml with payload data | |
| 18 | + | composeContent, err := rendererCompose.TXTRender(map[string]string{ | |
| 19 | + | "topdomain": topDomain, | |
| 20 | + | }) | |
| 21 | + | if err != nil { | |
| 22 | + | return err | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | // Save the rendered content to a file | |
| 26 | + | err = os.WriteFile("traefik-compose.yml", []byte(composeContent), 0644) | |
| 27 | + | if err != nil { | |
| 28 | + | return err | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | // Setup the TXT renderer for traefik.goyml | |
| 32 | + | rendererTraefik, err := templating.GetTXTRenderer("traefik.goyml", nil, logger) | |
| 33 | + | if err != nil { | |
| 34 | + | return err | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | // Render the traefik.goyml with payload data | |
| 38 | + | traefikContent, err := rendererTraefik.TXTRender(map[string]string{ | |
| 39 | + | "admin.email": adminEmail, | |
| 40 | + | }) | |
| 41 | + | if err != nil { | |
| 42 | + | return err | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | // Save the rendered content to a file | |
| 46 | + | err = os.WriteFile("traefik/traefik.yml", []byte(traefikContent), 0644) | |
| 47 | + | if err != nil { | |
| 48 | + | return err | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | return nil | |
| 52 | + | } | |
| @@ -1,1 +1,52 @@ | |||||
| 1 | package scripts | 1 | package scripts | ||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "os" | |||
| 5 | + | ||||
| 6 | + | "gogs.bnema.dev/gordon-echo/pkg/templating" | |||
| 7 | + | "gogs.bnema.dev/gordon-echo/pkg/utils" | |||
| 8 | + | ) | |||
| 9 | + | ||||
| 10 | + | func CreateYAMLFiles(topDomain, adminEmail string, logger *utils.Logger) error { | |||
| 11 | + | // Setup the TXT renderer for traefik-compose.goyml | |||
| 12 | + | rendererCompose, err := templating.GetTXTRenderer("traefik-compose.goyml", nil, logger) | |||
| 13 | + | if err != nil { | |||
| 14 | + | return err | |||
| 15 | + | } | |||
| 16 | + | ||||
| 17 | + | // Render the traefik-compose.goyml with payload data | |||
| 18 | + | composeContent, err := rendererCompose.TXTRender(map[string]string{ | |||
| 19 | + | "topdomain": topDomain, | |||
| 20 | + | }) | |||
| 21 | + | if err != nil { | |||
| 22 | + | return err | |||
| 23 | + | } | |||
| 24 | + | ||||
| 25 | + | // Save the rendered content to a file | |||
| 26 | + | err = os.WriteFile("traefik-compose.yml", []byte(composeContent), 0644) | |||
| 27 | + | if err != nil { | |||
| 28 | + | return err | |||
| 29 | + | } | |||
| 30 | + | ||||
| 31 | + | // Setup the TXT renderer for traefik.goyml | |||
| 32 | + | rendererTraefik, err := templating.GetTXTRenderer("traefik.goyml", nil, logger) | |||
| 33 | + | if err != nil { | |||
| 34 | + | return err | |||
| 35 | + | } | |||
| 36 | + | ||||
| 37 | + | // Render the traefik.goyml with payload data | |||
| 38 | + | traefikContent, err := rendererTraefik.TXTRender(map[string]string{ | |||
| 39 | + | "admin.email": adminEmail, | |||
| 40 | + | }) | |||
| 41 | + | if err != nil { | |||
| 42 | + | return err | |||
| 43 | + | } | |||
| 44 | + | ||||
| 45 | + | // Save the rendered content to a file | |||
| 46 | + | err = os.WriteFile("traefik/traefik.yml", []byte(traefikContent), 0644) | |||
| 47 | + | if err != nil { | |||
| 48 | + | return err | |||
| 49 | + | } | |||
| 50 | + | ||||
| 51 | + | return nil | |||
| 52 | + | } | |||
M pkg/utils/html-fragments.go +7 -3
| @@ -5,13 +5,17 @@ import ( | |||
| 5 | 5 | "fmt" | |
| 6 | 6 | ||
| 7 | 7 | "github.com/PuerkitoBio/goquery" | |
| 8 | - | "gogs.bnema.dev/gordon-echo/internal/ui" | |
| 8 | + | "gogs.bnema.dev/gordon-echo/config" | |
| 9 | 9 | ) | |
| 10 | 10 | ||
| 11 | + | type AppConfig struct { | |
| 12 | + | Config config.Provider | |
| 13 | + | } | |
| 14 | + | ||
| 11 | 15 | // GetHTMLFragmentByID returns the HTML fragment with the specified id | |
| 12 | - | func GetHTMLFragmentByID(id string, data interface{}) (string, error) { | |
| 16 | + | func (a *AppConfig) GetHTMLFragmentByID(id string, data interface{}) (string, error) { | |
| 13 | 17 | // 1. Render the template | |
| 14 | - | renderer, err := GetRenderer("components.gohtml", ui.TemplateFS, NewLogger()) | |
| 18 | + | renderer, err := GetRenderer("components.gohtml", a.Config.GetTemplateFS(), NewLogger()) | |
| 15 | 19 | if err != nil { | |
| 16 | 20 | return "", fmt.Errorf("failed to get renderer: %w", err) | |
| 17 | 21 | } | |
| @@ -5,13 +5,17 @@ import ( | |||||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | 6 | ||||
| 7 | "github.com/PuerkitoBio/goquery" | 7 | "github.com/PuerkitoBio/goquery" | ||
| 8 | - | "gogs.bnema.dev/gordon-echo/internal/ui" | 8 | + | "gogs.bnema.dev/gordon-echo/config" |
| 9 | ) | 9 | ) | ||
| 10 | 10 | ||||
| 11 | + | type AppConfig struct { | |||
| 12 | + | Config config.Provider | |||
| 13 | + | } | |||
| 14 | + | ||||
| 11 | // GetHTMLFragmentByID returns the HTML fragment with the specified id | 15 | // GetHTMLFragmentByID returns the HTML fragment with the specified id | ||
| 12 | - | func GetHTMLFragmentByID(id string, data interface{}) (string, error) { | 16 | + | func (a *AppConfig) GetHTMLFragmentByID(id string, data interface{}) (string, error) { |
| 13 | // 1. Render the template | 17 | // 1. Render the template | ||
| 14 | - | renderer, err := GetRenderer("components.gohtml", ui.TemplateFS, NewLogger()) | 18 | + | renderer, err := GetRenderer("components.gohtml", a.Config.GetTemplateFS(), NewLogger()) |
| 15 | if err != nil { | 19 | if err != nil { | ||
| 16 | return "", fmt.Errorf("failed to get renderer: %w", err) | 20 | return "", fmt.Errorf("failed to get renderer: %w", err) | ||
| 17 | } | 21 | } | ||
M pkg/utils/html-render.go +7 -0
| @@ -39,6 +39,13 @@ func (r *Renderer) Render(data interface{}) (string, error) { | |||
| 39 | 39 | ||
| 40 | 40 | // GetRenderer function returns a new Renderer instance | |
| 41 | 41 | func GetRenderer(filename string, fs fs.FS, logger *Logger) (*Renderer, error) { | |
| 42 | + | // Check if the file exists in the provided fs.FS using fs.Open | |
| 43 | + | file, err := fs.Open(filename) | |
| 44 | + | if err != nil { | |
| 45 | + | logger.Error().Err(err).Msg("Template or model '%s' not found" + filename) | |
| 46 | + | return nil, err | |
| 47 | + | } | |
| 48 | + | file.Close() // Close the file after checking | |
| 42 | 49 | tmpl, err := template.New(filename).ParseFS(fs, filename) | |
| 43 | 50 | if err != nil { | |
| 44 | 51 | logger.Error().Err(err).Msg("Failed to parse template") | |
| @@ -39,6 +39,13 @@ func (r *Renderer) Render(data interface{}) (string, error) { | |||||
| 39 | 39 | ||||
| 40 | // GetRenderer function returns a new Renderer instance | 40 | // GetRenderer function returns a new Renderer instance | ||
| 41 | func GetRenderer(filename string, fs fs.FS, logger *Logger) (*Renderer, error) { | 41 | func GetRenderer(filename string, fs fs.FS, logger *Logger) (*Renderer, error) { | ||
| 42 | + | // Check if the file exists in the provided fs.FS using fs.Open | |||
| 43 | + | file, err := fs.Open(filename) | |||
| 44 | + | if err != nil { | |||
| 45 | + | logger.Error().Err(err).Msg("Template or model '%s' not found" + filename) | |||
| 46 | + | return nil, err | |||
| 47 | + | } | |||
| 48 | + | file.Close() // Close the file after checking | |||
| 42 | tmpl, err := template.New(filename).ParseFS(fs, filename) | 49 | tmpl, err := template.New(filename).ParseFS(fs, filename) | ||
| 43 | if err != nil { | 50 | if err != nil { | ||
| 44 | logger.Error().Err(err).Msg("Failed to parse template") | 51 | logger.Error().Err(err).Msg("Failed to parse template") | ||
M pkg/utils/parser.go +4 -5
| @@ -6,7 +6,6 @@ import ( | |||
| 6 | 6 | "os" | |
| 7 | 7 | ||
| 8 | 8 | "github.com/tidwall/gjson" | |
| 9 | - | "gogs.bnema.dev/gordon-echo/internal/ui" | |
| 10 | 9 | "gopkg.in/yaml.v3" | |
| 11 | 10 | ) | |
| 12 | 11 | ||
| @@ -19,11 +18,11 @@ type YamlData struct { | |||
| 19 | 18 | } | |
| 20 | 19 | ||
| 21 | 20 | // ReadDataFromYAML loads the data from the given YAML file | |
| 22 | - | func ReadDataFromYAML() (YamlData, error) { | |
| 21 | + | func (a *AppConfig) ReadDataFromYAML() (YamlData, error) { | |
| 23 | 22 | var data YamlData | |
| 24 | 23 | ||
| 25 | 24 | // Open the YAML file inside the ui/components directory | |
| 26 | - | file, err := ui.TemplateFS.Open("strings.yaml") | |
| 25 | + | file, err := a.Config.GetTemplateFS().Open("strings.yaml") | |
| 27 | 26 | if err != nil { | |
| 28 | 27 | return data, fmt.Errorf("failed to open strings.yaml: %w", err) | |
| 29 | 28 | } | |
| @@ -44,8 +43,8 @@ func ReadDataFromYAML() (YamlData, error) { | |||
| 44 | 43 | } | |
| 45 | 44 | ||
| 46 | 45 | // PopulateDataFromYAML retrieves the data from the YAML file and sets the appropriate language data | |
| 47 | - | func PopulateDataFromYAML(currentLang string) (YamlData, error) { | |
| 48 | - | data, err := ReadDataFromYAML() | |
| 46 | + | func (a *AppConfig) PopulateDataFromYAML(currentLang string) (YamlData, error) { | |
| 47 | + | data, err := a.ReadDataFromYAML() | |
| 49 | 48 | if err != nil { | |
| 50 | 49 | return data, err | |
| 51 | 50 | } | |
| @@ -6,7 +6,6 @@ import ( | |||||
| 6 | "os" | 6 | "os" | ||
| 7 | 7 | ||||
| 8 | "github.com/tidwall/gjson" | 8 | "github.com/tidwall/gjson" | ||
| 9 | - | "gogs.bnema.dev/gordon-echo/internal/ui" | |||
| 10 | "gopkg.in/yaml.v3" | 9 | "gopkg.in/yaml.v3" | ||
| 11 | ) | 10 | ) | ||
| 12 | 11 | ||||
| @@ -19,11 +18,11 @@ type YamlData struct { | |||||
| 19 | } | 18 | } | ||
| 20 | 19 | ||||
| 21 | // ReadDataFromYAML loads the data from the given YAML file | 20 | // ReadDataFromYAML loads the data from the given YAML file | ||
| 22 | - | func ReadDataFromYAML() (YamlData, error) { | 21 | + | func (a *AppConfig) ReadDataFromYAML() (YamlData, error) { |
| 23 | var data YamlData | 22 | var data YamlData | ||
| 24 | 23 | ||||
| 25 | // Open the YAML file inside the ui/components directory | 24 | // Open the YAML file inside the ui/components directory | ||
| 26 | - | file, err := ui.TemplateFS.Open("strings.yaml") | 25 | + | file, err := a.Config.GetTemplateFS().Open("strings.yaml") |
| 27 | if err != nil { | 26 | if err != nil { | ||
| 28 | return data, fmt.Errorf("failed to open strings.yaml: %w", err) | 27 | return data, fmt.Errorf("failed to open strings.yaml: %w", err) | ||
| 29 | } | 28 | } | ||
| @@ -44,8 +43,8 @@ func ReadDataFromYAML() (YamlData, error) { | |||||
| 44 | } | 43 | } | ||
| 45 | 44 | ||||
| 46 | // PopulateDataFromYAML retrieves the data from the YAML file and sets the appropriate language data | 45 | // PopulateDataFromYAML retrieves the data from the YAML file and sets the appropriate language data | ||
| 47 | - | func PopulateDataFromYAML(currentLang string) (YamlData, error) { | 46 | + | func (a *AppConfig) PopulateDataFromYAML(currentLang string) (YamlData, error) { |
| 48 | - | data, err := ReadDataFromYAML() | 47 | + | data, err := a.ReadDataFromYAML() |
| 49 | if err != nil { | 48 | if err != nil { | ||
| 50 | return data, err | 49 | return data, err | ||
| 51 | } | 50 | } | ||
M pkg/utils/txt-render.go +9 -1
| @@ -38,8 +38,16 @@ func (r *TXTRenderer) TXTRender(data interface{}) (string, error) { | |||
| 38 | 38 | return buf.String(), nil | |
| 39 | 39 | } | |
| 40 | 40 | ||
| 41 | - | // GetRenderer function returns a new Renderer instance | |
| 41 | + | // GetTXTRenderer function returns a new Renderer instance | |
| 42 | 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 | + | ||
| 43 | 51 | mdls, err := template.New(filename).ParseFS(fs, filename) | |
| 44 | 52 | if err != nil { | |
| 45 | 53 | logger.Error().Err(err).Msg("Failed to parse template") | |
| @@ -38,8 +38,16 @@ func (r *TXTRenderer) TXTRender(data interface{}) (string, error) { | |||||
| 38 | return buf.String(), nil | 38 | return buf.String(), nil | ||
| 39 | } | 39 | } | ||
| 40 | 40 | ||||
| 41 | - | // GetRenderer function returns a new Renderer instance | 41 | + | // GetTXTRenderer function returns a new Renderer instance |
| 42 | func GetTXTRenderer(filename string, fs fs.FS, logger *Logger) (*TXTRenderer, error) { | 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 | + | ||||
| 43 | mdls, err := template.New(filename).ParseFS(fs, filename) | 51 | mdls, err := template.New(filename).ParseFS(fs, filename) | ||
| 44 | if err != nil { | 52 | if err != nil { | ||
| 45 | logger.Error().Err(err).Msg("Failed to parse template") | 53 | logger.Error().Err(err).Msg("Failed to parse template") | ||