yaml parser and languages WIP
- commit
- cd54f111ea1e71e7751233b588477a9fc2ad54bd
- author
- bnema · · 2023-09-02 05:12 UTC
- parent
- df8fa86
- signature
- unsigned
7 files · +181 -42
M cmd/main.go +89 -29
| @@ -3,57 +3,60 @@ package main | |||
| 3 | 3 | import ( | |
| 4 | 4 | "fmt" | |
| 5 | 5 | "html/template" | |
| 6 | + | "io/fs" | |
| 6 | 7 | "net/http" | |
| 7 | - | "strings" | |
| 8 | + | "os" | |
| 8 | 9 | ||
| 9 | 10 | "github.com/labstack/echo/v4" | |
| 11 | + | "github.com/labstack/echo/v4/middleware" | |
| 12 | + | "github.com/rs/zerolog" | |
| 10 | 13 | "gogs.bnema.dev/gordon-echo/internal/ui" | |
| 11 | 14 | "gogs.bnema.dev/gordon-echo/pkg/utils" | |
| 12 | 15 | ) | |
| 13 | 16 | ||
| 14 | - | const trailedAdminPath = "/admin/" | |
| 17 | + | // getRenderer returns a new renderer for the given template | |
| 18 | + | func getRenderer(filename string, fs fs.FS) (*utils.Renderer, error) { | |
| 19 | + | tmpl, err := template.New(filename).ParseFS(fs, filename) | |
| 20 | + | if err != nil { | |
| 21 | + | return nil, err | |
| 22 | + | } | |
| 23 | + | return &utils.Renderer{ | |
| 24 | + | Template: tmpl, | |
| 25 | + | }, nil | |
| 26 | + | } | |
| 15 | 27 | ||
| 16 | 28 | // bindStaticAdminUI serves the static admin UI | |
| 17 | 29 | func bindStaticAdminUI(e *echo.Echo) error { | |
| 18 | - | tmpl, err := template.New("index").ParseFS(ui.PublicFS, "index.html") | |
| 30 | + | // Load static website data from YAML | |
| 31 | + | staticData, err := utils.LoadDataFromYAML() | |
| 19 | 32 | if err != nil { | |
| 20 | - | return fmt.Errorf("failed to parse index.html: %w", err) | |
| 33 | + | return err | |
| 21 | 34 | } | |
| 22 | 35 | ||
| 23 | - | renderer := &utils.Renderer{ | |
| 24 | - | Template: tmpl, | |
| 25 | - | ParseError: err, | |
| 26 | - | } | |
| 36 | + | // Main handler for /admin | |
| 37 | + | e.GET("/admin/:lang", func(c echo.Context) error { | |
| 38 | + | lang := c.Param("lang") | |
| 27 | 39 | ||
| 28 | - | // redirect to trailing slash to ensure that relative urls will still work properly | |
| 29 | - | e.GET( | |
| 30 | - | strings.TrimRight(trailedAdminPath, "/"), | |
| 31 | - | func(c echo.Context) error { | |
| 32 | - | return c.Redirect(http.StatusTemporaryRedirect, strings.TrimLeft(trailedAdminPath, "/")) | |
| 33 | - | }, | |
| 34 | - | ) | |
| 35 | - | ||
| 36 | - | // Main handler for /admin/ | |
| 37 | - | e.GET(trailedAdminPath, func(c echo.Context) error { | |
| 38 | - | data := map[string]interface{}{ | |
| 39 | - | "website": map[string]interface{}{ | |
| 40 | - | "title": "Hello, World!", | |
| 41 | - | }, | |
| 40 | + | switch lang { | |
| 41 | + | case "fr": | |
| 42 | + | staticData.CurrentLang = staticData.FR | |
| 43 | + | default: // default to English if no match | |
| 44 | + | staticData.CurrentLang = staticData.EN | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | renderer, err := getRenderer("index.html", ui.PublicFS) | |
| 48 | + | if err != nil { | |
| 49 | + | return err | |
| 42 | 50 | } | |
| 43 | 51 | ||
| 44 | - | html, err := renderer.Render(data) | |
| 52 | + | html, err := renderer.Render(staticData) | |
| 45 | 53 | if err != nil { | |
| 46 | 54 | return c.String(http.StatusInternalServerError, fmt.Sprintf("Failed to render template: %v", err)) | |
| 47 | 55 | } | |
| 48 | 56 | ||
| 49 | 57 | return c.HTML(http.StatusOK, html) | |
| 50 | 58 | }) | |
| 51 | - | // serves static files from the /ui/dist directory | |
| 52 | - | // (similar to echo.StaticFS but with gzip middleware enabled) | |
| 53 | - | e.GET( | |
| 54 | - | trailedAdminPath+"*", | |
| 55 | - | echo.StaticDirectoryHandler(ui.PublicFS, false), | |
| 56 | - | ) | |
| 59 | + | ||
| 57 | 60 | e.GET("/htmx", func(c echo.Context) error { | |
| 58 | 61 | fragment := c.Request().Header.Get("X-Fragment") | |
| 59 | 62 | ||
| @@ -70,14 +73,71 @@ func bindStaticAdminUI(e *echo.Echo) error { | |||
| 70 | 73 | // Handle other fragments or default behavior | |
| 71 | 74 | return c.String(http.StatusBadRequest, "Invalid fragment") | |
| 72 | 75 | }) | |
| 76 | + | ||
| 77 | + | // !!! IMPORTANT !!! This must be the last route (except for the 404 route) | |
| 78 | + | e.GET("/*", func(c echo.Context) error { | |
| 79 | + | // Set cache headers | |
| 80 | + | c.Response().Header().Set("Cache-Control", "public, max-age=86400") | |
| 81 | + | ||
| 82 | + | // Proceed with serving the file | |
| 83 | + | return echo.StaticDirectoryHandler(ui.PublicFS, false)(c) | |
| 84 | + | }) | |
| 85 | + | ||
| 86 | + | // // 404 handler | |
| 87 | + | // e.HTTPErrorHandler = func(err error, c echo.Context) { | |
| 88 | + | // if he, ok := err.(*echo.HTTPError); ok { | |
| 89 | + | // if he.Code == http.StatusNotFound { | |
| 90 | + | // // 404.html | |
| 91 | + | // renderer, err := getRenderer("404.html", ui.TemplateFS) | |
| 92 | + | // if err != nil { | |
| 93 | + | // c.String(http.StatusInternalServerError, fmt.Sprintf("Failed to render template: %v", err)) | |
| 94 | + | // } | |
| 95 | + | ||
| 96 | + | // data := map[string]interface{}{ | |
| 97 | + | // "website": map[string]interface{}{ | |
| 98 | + | // "title": "Page Not Found", | |
| 99 | + | // }, | |
| 100 | + | // } | |
| 101 | + | ||
| 102 | + | // html, err := renderer.Render(data) | |
| 103 | + | // if err != nil { | |
| 104 | + | // c.String(http.StatusInternalServerError, fmt.Sprintf("Failed to render template: %v", err)) | |
| 105 | + | // } | |
| 106 | + | ||
| 107 | + | // c.HTML(http.StatusNotFound, html) | |
| 108 | + | // return | |
| 109 | + | // } | |
| 110 | + | // } | |
| 111 | + | ||
| 112 | + | // e.DefaultHTTPErrorHandler(err, c) | |
| 113 | + | // } | |
| 114 | + | ||
| 73 | 115 | return nil | |
| 116 | + | ||
| 74 | 117 | } | |
| 75 | 118 | ||
| 76 | 119 | func main() { | |
| 120 | + | logger := zerolog.New(os.Stdout) | |
| 77 | 121 | e := echo.New() | |
| 78 | 122 | ||
| 123 | + | e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ | |
| 124 | + | LogURI: true, | |
| 125 | + | LogStatus: true, | |
| 126 | + | LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error { | |
| 127 | + | logger.Info(). | |
| 128 | + | Str("URI", v.URI). | |
| 129 | + | Int("status", v.Status). | |
| 130 | + | Msg("request") | |
| 131 | + | ||
| 132 | + | return nil | |
| 133 | + | }, | |
| 134 | + | })) | |
| 135 | + | ||
| 79 | 136 | // use func bindStaticAdminUI to serve the static admin UI | |
| 80 | 137 | bindStaticAdminUI(e) | |
| 138 | + | if err := bindStaticAdminUI(e); err != nil { | |
| 139 | + | e.Logger.Fatal(err) | |
| 140 | + | } | |
| 81 | 141 | ||
| 82 | 142 | e.Logger.Fatal(e.Start(":1323")) | |
| 83 | 143 | ||
| @@ -3,57 +3,60 @@ package main | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "fmt" | 4 | "fmt" | ||
| 5 | "html/template" | 5 | "html/template" | ||
| 6 | + | "io/fs" | |||
| 6 | "net/http" | 7 | "net/http" | ||
| 7 | - | "strings" | 8 | + | "os" |
| 8 | 9 | ||||
| 9 | "github.com/labstack/echo/v4" | 10 | "github.com/labstack/echo/v4" | ||
| 11 | + | "github.com/labstack/echo/v4/middleware" | |||
| 12 | + | "github.com/rs/zerolog" | |||
| 10 | "gogs.bnema.dev/gordon-echo/internal/ui" | 13 | "gogs.bnema.dev/gordon-echo/internal/ui" | ||
| 11 | "gogs.bnema.dev/gordon-echo/pkg/utils" | 14 | "gogs.bnema.dev/gordon-echo/pkg/utils" | ||
| 12 | ) | 15 | ) | ||
| 13 | 16 | ||||
| 14 | - | const trailedAdminPath = "/admin/" | 17 | + | // getRenderer returns a new renderer for the given template |
| 18 | + | func getRenderer(filename string, fs fs.FS) (*utils.Renderer, error) { | |||
| 19 | + | tmpl, err := template.New(filename).ParseFS(fs, filename) | |||
| 20 | + | if err != nil { | |||
| 21 | + | return nil, err | |||
| 22 | + | } | |||
| 23 | + | return &utils.Renderer{ | |||
| 24 | + | Template: tmpl, | |||
| 25 | + | }, nil | |||
| 26 | + | } | |||
| 15 | 27 | ||||
| 16 | // bindStaticAdminUI serves the static admin UI | 28 | // bindStaticAdminUI serves the static admin UI | ||
| 17 | func bindStaticAdminUI(e *echo.Echo) error { | 29 | func bindStaticAdminUI(e *echo.Echo) error { | ||
| 18 | - | tmpl, err := template.New("index").ParseFS(ui.PublicFS, "index.html") | 30 | + | // Load static website data from YAML |
| 31 | + | staticData, err := utils.LoadDataFromYAML() | |||
| 19 | if err != nil { | 32 | if err != nil { | ||
| 20 | - | return fmt.Errorf("failed to parse index.html: %w", err) | 33 | + | return err |
| 21 | } | 34 | } | ||
| 22 | 35 | ||||
| 23 | - | renderer := &utils.Renderer{ | 36 | + | // Main handler for /admin |
| 24 | - | Template: tmpl, | 37 | + | e.GET("/admin/:lang", func(c echo.Context) error { |
| 25 | - | ParseError: err, | 38 | + | lang := c.Param("lang") |
| 26 | - | } | |||
| 27 | 39 | ||||
| 28 | - | // redirect to trailing slash to ensure that relative urls will still work properly | 40 | + | switch lang { |
| 29 | - | e.GET( | 41 | + | case "fr": |
| 30 | - | strings.TrimRight(trailedAdminPath, "/"), | 42 | + | staticData.CurrentLang = staticData.FR |
| 31 | - | func(c echo.Context) error { | 43 | + | default: // default to English if no match |
| 32 | - | return c.Redirect(http.StatusTemporaryRedirect, strings.TrimLeft(trailedAdminPath, "/")) | 44 | + | staticData.CurrentLang = staticData.EN |
| 33 | - | }, | 45 | + | } |
| 34 | - | ) | 46 | + | |
| 35 | - | 47 | + | renderer, err := getRenderer("index.html", ui.PublicFS) | |
| 36 | - | // Main handler for /admin/ | 48 | + | if err != nil { |
| 37 | - | e.GET(trailedAdminPath, func(c echo.Context) error { | 49 | + | return err |
| 38 | - | data := map[string]interface{}{ | |||
| 39 | - | "website": map[string]interface{}{ | |||
| 40 | - | "title": "Hello, World!", | |||
| 41 | - | }, | |||
| 42 | } | 50 | } | ||
| 43 | 51 | ||||
| 44 | - | html, err := renderer.Render(data) | 52 | + | html, err := renderer.Render(staticData) |
| 45 | if err != nil { | 53 | if err != nil { | ||
| 46 | return c.String(http.StatusInternalServerError, fmt.Sprintf("Failed to render template: %v", err)) | 54 | return c.String(http.StatusInternalServerError, fmt.Sprintf("Failed to render template: %v", err)) | ||
| 47 | } | 55 | } | ||
| 48 | 56 | ||||
| 49 | return c.HTML(http.StatusOK, html) | 57 | return c.HTML(http.StatusOK, html) | ||
| 50 | }) | 58 | }) | ||
| 51 | - | // serves static files from the /ui/dist directory | 59 | + | |
| 52 | - | // (similar to echo.StaticFS but with gzip middleware enabled) | |||
| 53 | - | e.GET( | |||
| 54 | - | trailedAdminPath+"*", | |||
| 55 | - | echo.StaticDirectoryHandler(ui.PublicFS, false), | |||
| 56 | - | ) | |||
| 57 | e.GET("/htmx", func(c echo.Context) error { | 60 | e.GET("/htmx", func(c echo.Context) error { | ||
| 58 | fragment := c.Request().Header.Get("X-Fragment") | 61 | fragment := c.Request().Header.Get("X-Fragment") | ||
| 59 | 62 | ||||
| @@ -70,14 +73,71 @@ func bindStaticAdminUI(e *echo.Echo) error { | |||||
| 70 | // Handle other fragments or default behavior | 73 | // Handle other fragments or default behavior | ||
| 71 | return c.String(http.StatusBadRequest, "Invalid fragment") | 74 | return c.String(http.StatusBadRequest, "Invalid fragment") | ||
| 72 | }) | 75 | }) | ||
| 76 | + | ||||
| 77 | + | // !!! IMPORTANT !!! This must be the last route (except for the 404 route) | |||
| 78 | + | e.GET("/*", func(c echo.Context) error { | |||
| 79 | + | // Set cache headers | |||
| 80 | + | c.Response().Header().Set("Cache-Control", "public, max-age=86400") | |||
| 81 | + | ||||
| 82 | + | // Proceed with serving the file | |||
| 83 | + | return echo.StaticDirectoryHandler(ui.PublicFS, false)(c) | |||
| 84 | + | }) | |||
| 85 | + | ||||
| 86 | + | // // 404 handler | |||
| 87 | + | // e.HTTPErrorHandler = func(err error, c echo.Context) { | |||
| 88 | + | // if he, ok := err.(*echo.HTTPError); ok { | |||
| 89 | + | // if he.Code == http.StatusNotFound { | |||
| 90 | + | // // 404.html | |||
| 91 | + | // renderer, err := getRenderer("404.html", ui.TemplateFS) | |||
| 92 | + | // if err != nil { | |||
| 93 | + | // c.String(http.StatusInternalServerError, fmt.Sprintf("Failed to render template: %v", err)) | |||
| 94 | + | // } | |||
| 95 | + | ||||
| 96 | + | // data := map[string]interface{}{ | |||
| 97 | + | // "website": map[string]interface{}{ | |||
| 98 | + | // "title": "Page Not Found", | |||
| 99 | + | // }, | |||
| 100 | + | // } | |||
| 101 | + | ||||
| 102 | + | // html, err := renderer.Render(data) | |||
| 103 | + | // if err != nil { | |||
| 104 | + | // c.String(http.StatusInternalServerError, fmt.Sprintf("Failed to render template: %v", err)) | |||
| 105 | + | // } | |||
| 106 | + | ||||
| 107 | + | // c.HTML(http.StatusNotFound, html) | |||
| 108 | + | // return | |||
| 109 | + | // } | |||
| 110 | + | // } | |||
| 111 | + | ||||
| 112 | + | // e.DefaultHTTPErrorHandler(err, c) | |||
| 113 | + | // } | |||
| 114 | + | ||||
| 73 | return nil | 115 | return nil | ||
| 116 | + | ||||
| 74 | } | 117 | } | ||
| 75 | 118 | ||||
| 76 | func main() { | 119 | func main() { | ||
| 120 | + | logger := zerolog.New(os.Stdout) | |||
| 77 | e := echo.New() | 121 | e := echo.New() | ||
| 78 | 122 | ||||
| 123 | + | e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{ | |||
| 124 | + | LogURI: true, | |||
| 125 | + | LogStatus: true, | |||
| 126 | + | LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error { | |||
| 127 | + | logger.Info(). | |||
| 128 | + | Str("URI", v.URI). | |||
| 129 | + | Int("status", v.Status). | |||
| 130 | + | Msg("request") | |||
| 131 | + | ||||
| 132 | + | return nil | |||
| 133 | + | }, | |||
| 134 | + | })) | |||
| 135 | + | ||||
| 79 | // use func bindStaticAdminUI to serve the static admin UI | 136 | // use func bindStaticAdminUI to serve the static admin UI | ||
| 80 | bindStaticAdminUI(e) | 137 | bindStaticAdminUI(e) | ||
| 138 | + | if err := bindStaticAdminUI(e); err != nil { | |||
| 139 | + | e.Logger.Fatal(err) | |||
| 140 | + | } | |||
| 81 | 141 | ||||
| 82 | e.Logger.Fatal(e.Start(":1323")) | 142 | e.Logger.Fatal(e.Start(":1323")) | ||
| 83 | 143 | ||||
M go.mod +10 -4
| @@ -2,19 +2,25 @@ module gogs.bnema.dev/gordon-echo | |||
| 2 | 2 | ||
| 3 | 3 | go 1.21.0 | |
| 4 | 4 | ||
| 5 | - | require github.com/labstack/echo/v4 v4.11.1 | |
| 5 | + | require ( | |
| 6 | + | github.com/PuerkitoBio/goquery v1.8.1 | |
| 7 | + | github.com/labstack/echo/v4 v4.11.1 | |
| 8 | + | ) | |
| 6 | 9 | ||
| 7 | 10 | require ( | |
| 8 | - | github.com/PuerkitoBio/goquery v1.8.1 // indirect | |
| 9 | 11 | github.com/andybalholm/cascadia v1.3.1 // indirect | |
| 10 | - | github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect | |
| 12 | + | github.com/golang-jwt/jwt v3.2.2+incompatible // indirect | |
| 13 | + | github.com/google/safehtml v0.1.0 // indirect | |
| 14 | + | golang.org/x/time v0.3.0 // indirect | |
| 15 | + | gopkg.in/yaml.v3 v3.0.1 // indirect | |
| 11 | 16 | ) | |
| 12 | 17 | ||
| 13 | 18 | require ( | |
| 14 | - | github.com/labstack/echo v3.3.10+incompatible | |
| 19 | + | github.com/go-chi/chi v1.5.4 | |
| 15 | 20 | github.com/labstack/gommon v0.4.0 // indirect | |
| 16 | 21 | github.com/mattn/go-colorable v0.1.13 // indirect | |
| 17 | 22 | github.com/mattn/go-isatty v0.0.19 // indirect | |
| 23 | + | github.com/rs/zerolog v1.30.0 | |
| 18 | 24 | github.com/valyala/bytebufferpool v1.0.0 // indirect | |
| 19 | 25 | github.com/valyala/fasttemplate v1.2.2 // indirect | |
| 20 | 26 | golang.org/x/crypto v0.11.0 // indirect | |
| @@ -2,19 +2,25 @@ module gogs.bnema.dev/gordon-echo | |||||
| 2 | 2 | ||||
| 3 | go 1.21.0 | 3 | go 1.21.0 | ||
| 4 | 4 | ||||
| 5 | - | require github.com/labstack/echo/v4 v4.11.1 | 5 | + | require ( |
| 6 | + | github.com/PuerkitoBio/goquery v1.8.1 | |||
| 7 | + | github.com/labstack/echo/v4 v4.11.1 | |||
| 8 | + | ) | |||
| 6 | 9 | ||||
| 7 | require ( | 10 | require ( | ||
| 8 | - | github.com/PuerkitoBio/goquery v1.8.1 // indirect | |||
| 9 | github.com/andybalholm/cascadia v1.3.1 // indirect | 11 | github.com/andybalholm/cascadia v1.3.1 // indirect | ||
| 10 | - | github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect | 12 | + | github.com/golang-jwt/jwt v3.2.2+incompatible // indirect |
| 13 | + | github.com/google/safehtml v0.1.0 // indirect | |||
| 14 | + | golang.org/x/time v0.3.0 // indirect | |||
| 15 | + | gopkg.in/yaml.v3 v3.0.1 // indirect | |||
| 11 | ) | 16 | ) | ||
| 12 | 17 | ||||
| 13 | require ( | 18 | require ( | ||
| 14 | - | github.com/labstack/echo v3.3.10+incompatible | 19 | + | github.com/go-chi/chi v1.5.4 |
| 15 | github.com/labstack/gommon v0.4.0 // indirect | 20 | github.com/labstack/gommon v0.4.0 // indirect | ||
| 16 | github.com/mattn/go-colorable v0.1.13 // indirect | 21 | github.com/mattn/go-colorable v0.1.13 // indirect | ||
| 17 | github.com/mattn/go-isatty v0.0.19 // indirect | 22 | github.com/mattn/go-isatty v0.0.19 // indirect | ||
| 23 | + | github.com/rs/zerolog v1.30.0 | |||
| 18 | github.com/valyala/bytebufferpool v1.0.0 // indirect | 24 | github.com/valyala/bytebufferpool v1.0.0 // indirect | ||
| 19 | github.com/valyala/fasttemplate v1.2.2 // indirect | 25 | github.com/valyala/fasttemplate v1.2.2 // indirect | ||
| 20 | golang.org/x/crypto v0.11.0 // indirect | 26 | golang.org/x/crypto v0.11.0 // indirect | ||
M go.sum +21 -4 generated file: collapsed
A internal/ui/public/assets/imgs/gordon-mascot.png +0 -0 binary file: no text diff
binary file: no text diff
A internal/ui/templates/strings.yaml +9 -0
| @@ -0,0 +1,9 @@ | |||
| 1 | + | en: | |
| 2 | + | website: | |
| 3 | + | title: Admin Dashboard | |
| 4 | + | user: John Doe | |
| 5 | + | ||
| 6 | + | fr: | |
| 7 | + | website: | |
| 8 | + | title: Tableau de bord administrateur | |
| 9 | + | user: Jean Dupont⏎̸ | |
| @@ -0,0 +1,9 @@ | |||||
| 1 | + | en: | |||
| 2 | + | website: | |||
| 3 | + | title: Admin Dashboard | |||
| 4 | + | user: John Doe | |||
| 5 | + | ||||
| 6 | + | fr: | |||
| 7 | + | website: | |||
| 8 | + | title: Tableau de bord administrateur | |||
| 9 | + | user: Jean Dupont⏎̸ | |||
A pkg/utils/parser.go +49 -0
| @@ -0,0 +1,49 @@ | |||
| 1 | + | package utils | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "fmt" | |
| 5 | + | "io" | |
| 6 | + | ||
| 7 | + | "gogs.bnema.dev/gordon-echo/internal/ui" | |
| 8 | + | "gopkg.in/yaml.v3" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | type Data struct { | |
| 12 | + | EN StaticData `yaml:"en"` | |
| 13 | + | FR StaticData `yaml:"fr"` | |
| 14 | + | CurrentLang StaticData `yaml:"-"` | |
| 15 | + | Website struct { | |
| 16 | + | Title string `yaml:"title"` | |
| 17 | + | User string `yaml:"user"` | |
| 18 | + | } `yaml:"website"` | |
| 19 | + | } | |
| 20 | + | ||
| 21 | + | type StaticData struct { | |
| 22 | + | Title string `yaml:"title"` | |
| 23 | + | User string `yaml:"user"` | |
| 24 | + | } | |
| 25 | + | ||
| 26 | + | // LoadDataFromYAML loads the data from the given YAML file | |
| 27 | + | func LoadDataFromYAML() (Data, error) { | |
| 28 | + | var data Data | |
| 29 | + | ||
| 30 | + | // Open the YAML file inside the ui/components directory | |
| 31 | + | file, err := ui.TemplateFS.Open("strings.yaml") | |
| 32 | + | if err != nil { | |
| 33 | + | return data, fmt.Errorf("failed to open strings.yaml: %w", err) | |
| 34 | + | } | |
| 35 | + | defer file.Close() | |
| 36 | + | ||
| 37 | + | // Read the content of the file | |
| 38 | + | content, err := io.ReadAll(file) | |
| 39 | + | if err != nil { | |
| 40 | + | return data, fmt.Errorf("failed to read strings.yaml: %w", err) | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | // Unmarshal the YAML content into the Data struct | |
| 44 | + | if err := yaml.Unmarshal(content, &data); err != nil { | |
| 45 | + | return data, fmt.Errorf("failed to unmarshal strings.yaml: %w", err) | |
| 46 | + | } | |
| 47 | + | ||
| 48 | + | return data, nil | |
| 49 | + | } | |
| @@ -0,0 +1,49 @@ | |||||
| 1 | + | package utils | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "fmt" | |||
| 5 | + | "io" | |||
| 6 | + | ||||
| 7 | + | "gogs.bnema.dev/gordon-echo/internal/ui" | |||
| 8 | + | "gopkg.in/yaml.v3" | |||
| 9 | + | ) | |||
| 10 | + | ||||
| 11 | + | type Data struct { | |||
| 12 | + | EN StaticData `yaml:"en"` | |||
| 13 | + | FR StaticData `yaml:"fr"` | |||
| 14 | + | CurrentLang StaticData `yaml:"-"` | |||
| 15 | + | Website struct { | |||
| 16 | + | Title string `yaml:"title"` | |||
| 17 | + | User string `yaml:"user"` | |||
| 18 | + | } `yaml:"website"` | |||
| 19 | + | } | |||
| 20 | + | ||||
| 21 | + | type StaticData struct { | |||
| 22 | + | Title string `yaml:"title"` | |||
| 23 | + | User string `yaml:"user"` | |||
| 24 | + | } | |||
| 25 | + | ||||
| 26 | + | // LoadDataFromYAML loads the data from the given YAML file | |||
| 27 | + | func LoadDataFromYAML() (Data, error) { | |||
| 28 | + | var data Data | |||
| 29 | + | ||||
| 30 | + | // Open the YAML file inside the ui/components directory | |||
| 31 | + | file, err := ui.TemplateFS.Open("strings.yaml") | |||
| 32 | + | if err != nil { | |||
| 33 | + | return data, fmt.Errorf("failed to open strings.yaml: %w", err) | |||
| 34 | + | } | |||
| 35 | + | defer file.Close() | |||
| 36 | + | ||||
| 37 | + | // Read the content of the file | |||
| 38 | + | content, err := io.ReadAll(file) | |||
| 39 | + | if err != nil { | |||
| 40 | + | return data, fmt.Errorf("failed to read strings.yaml: %w", err) | |||
| 41 | + | } | |||
| 42 | + | ||||
| 43 | + | // Unmarshal the YAML content into the Data struct | |||
| 44 | + | if err := yaml.Unmarshal(content, &data); err != nil { | |||
| 45 | + | return data, fmt.Errorf("failed to unmarshal strings.yaml: %w", err) | |||
| 46 | + | } | |||
| 47 | + | ||||
| 48 | + | return data, nil | |||
| 49 | + | } | |||
M pkg/utils/render.go +3 -5
| @@ -11,9 +11,8 @@ type Renderer struct { | |||
| 11 | 11 | ParseError error | |
| 12 | 12 | } | |
| 13 | 13 | ||
| 14 | - | // Render executes the template with the specified data as the dot object | |
| 15 | - | // and returns the result as plain string. | |
| 16 | - | func (r *Renderer) Render(data any) (string, error) { | |
| 14 | + | // Render function renders the template with the given data | |
| 15 | + | func (r *Renderer) Render(data interface{}) (string, error) { | |
| 17 | 16 | if r.ParseError != nil { | |
| 18 | 17 | return "", r.ParseError | |
| 19 | 18 | } | |
| @@ -23,10 +22,9 @@ func (r *Renderer) Render(data any) (string, error) { | |||
| 23 | 22 | } | |
| 24 | 23 | ||
| 25 | 24 | buf := new(bytes.Buffer) | |
| 26 | - | ||
| 27 | 25 | err := r.Template.Execute(buf, data) | |
| 28 | 26 | if err != nil { | |
| 29 | - | return "", errors.New("failed to execute template: " + err.Error()) | |
| 27 | + | return "", err | |
| 30 | 28 | } | |
| 31 | 29 | ||
| 32 | 30 | return buf.String(), nil | |
| @@ -11,9 +11,8 @@ type Renderer struct { | |||||
| 11 | ParseError error | 11 | ParseError error | ||
| 12 | } | 12 | } | ||
| 13 | 13 | ||||
| 14 | - | // Render executes the template with the specified data as the dot object | 14 | + | // Render function renders the template with the given data |
| 15 | - | // and returns the result as plain string. | 15 | + | func (r *Renderer) Render(data interface{}) (string, error) { |
| 16 | - | func (r *Renderer) Render(data any) (string, error) { | |||
| 17 | if r.ParseError != nil { | 16 | if r.ParseError != nil { | ||
| 18 | return "", r.ParseError | 17 | return "", r.ParseError | ||
| 19 | } | 18 | } | ||
| @@ -23,10 +22,9 @@ func (r *Renderer) Render(data any) (string, error) { | |||||
| 23 | } | 22 | } | ||
| 24 | 23 | ||||
| 25 | buf := new(bytes.Buffer) | 24 | buf := new(bytes.Buffer) | ||
| 26 | - | ||||
| 27 | err := r.Template.Execute(buf, data) | 25 | err := r.Template.Execute(buf, data) | ||
| 28 | if err != nil { | 26 | if err != nil { | ||
| 29 | - | return "", errors.New("failed to execute template: " + err.Error()) | 27 | + | return "", err |
| 30 | } | 28 | } | ||
| 31 | 29 | ||||
| 32 | return buf.String(), nil | 30 | return buf.String(), nil | ||