detached bfa189b

yaml parser and languages WIP

commit
bfa189bf38bd609be4e76ec6b6a41dc1608a8dee
author
bnema · · 2023-09-02 05:12 UTC
parent
4673abc
signature
unsigned
7 files · +181 -42
M cmd/main.go +89 -29
@@ -3,57 +3,60 @@ package main
33 import (
44 "fmt"
55 "html/template"
6+ "io/fs"
67 "net/http"
7- "strings"
8+ "os"
89
910 "github.com/labstack/echo/v4"
11+ "github.com/labstack/echo/v4/middleware"
12+ "github.com/rs/zerolog"
1013 "gogs.bnema.dev/gordon-echo/internal/ui"
1114 "gogs.bnema.dev/gordon-echo/pkg/utils"
1215 )
1316
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+}
1527
1628 // bindStaticAdminUI serves the static admin UI
1729 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()
1932 if err != nil {
20- return fmt.Errorf("failed to parse index.html: %w", err)
33+ return err
2134 }
2235
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")
2739
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
4250 }
4351
44- html, err := renderer.Render(data)
52+ html, err := renderer.Render(staticData)
4553 if err != nil {
4654 return c.String(http.StatusInternalServerError, fmt.Sprintf("Failed to render template: %v", err))
4755 }
4856
4957 return c.HTML(http.StatusOK, html)
5058 })
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+
5760 e.GET("/htmx", func(c echo.Context) error {
5861 fragment := c.Request().Header.Get("X-Fragment")
5962
@@ -70,14 +73,71 @@ func bindStaticAdminUI(e *echo.Echo) error {
7073 // Handle other fragments or default behavior
7174 return c.String(http.StatusBadRequest, "Invalid fragment")
7275 })
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+
73115 return nil
116+
74117 }
75118
76119 func main() {
120+ logger := zerolog.New(os.Stdout)
77121 e := echo.New()
78122
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+
79136 // use func bindStaticAdminUI to serve the static admin UI
80137 bindStaticAdminUI(e)
138+ if err := bindStaticAdminUI(e); err != nil {
139+ e.Logger.Fatal(err)
140+ }
81141
82142 e.Logger.Fatal(e.Start(":1323"))
83143
@@ -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 UI28 // 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 properly40+ 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 directory59+
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 behavior73 // 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 nil115 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 UI136 // 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
22
33 go 1.21.0
44
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+)
69
710 require (
8- github.com/PuerkitoBio/goquery v1.8.1 // indirect
911 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
1116 )
1217
1318 require (
14- github.com/labstack/echo v3.3.10+incompatible
19+ github.com/go-chi/chi v1.5.4
1520 github.com/labstack/gommon v0.4.0 // indirect
1621 github.com/mattn/go-colorable v0.1.13 // indirect
1722 github.com/mattn/go-isatty v0.0.19 // indirect
23+ github.com/rs/zerolog v1.30.0
1824 github.com/valyala/bytebufferpool v1.0.0 // indirect
1925 github.com/valyala/fasttemplate v1.2.2 // indirect
2026 golang.org/x/crypto v0.11.0 // indirect
@@ -2,19 +2,25 @@ module gogs.bnema.dev/gordon-echo
2 2
3 go 1.21.03 go 1.21.0
4 4
5-require github.com/labstack/echo/v4 v4.11.15+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 // indirect11 github.com/andybalholm/cascadia v1.3.1 // indirect
10- github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect12+ 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+incompatible19+ github.com/go-chi/chi v1.5.4
15 github.com/labstack/gommon v0.4.0 // indirect20 github.com/labstack/gommon v0.4.0 // indirect
16 github.com/mattn/go-colorable v0.1.13 // indirect21 github.com/mattn/go-colorable v0.1.13 // indirect
17 github.com/mattn/go-isatty v0.0.19 // indirect22 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 // indirect24 github.com/valyala/bytebufferpool v1.0.0 // indirect
19 github.com/valyala/fasttemplate v1.2.2 // indirect25 github.com/valyala/fasttemplate v1.2.2 // indirect
20 golang.org/x/crypto v0.11.0 // indirect26 golang.org/x/crypto v0.11.0 // indirect
M go.sum +21 -4
@@ -2,26 +2,39 @@ github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAc
22 github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
33 github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
44 github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
5+github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
56 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
68 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7-github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
8-github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
9-github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
10-github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
9+github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
10+github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
11+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
12+github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
13+github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
14+github.com/google/safehtml v0.1.0 h1:EwLKo8qawTKfsi0orxcQAZzu07cICaBeFMegAU9eaT8=
15+github.com/google/safehtml v0.1.0/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU=
1116 github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4=
1217 github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ=
1318 github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
1419 github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
1520 github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
21+github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
1622 github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
1723 github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
1824 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
1925 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
2026 github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
2127 github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
28+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
29+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
2230 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
31+github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
32+github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c=
33+github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w=
2334 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
2435 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
36+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
37+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
2538 github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
2639 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
2740 github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
@@ -66,6 +79,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
6679 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
6780 golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
6881 golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
82+golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
83+golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
6984 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
7085 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
7186 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
@@ -73,3 +88,5 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
7388 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
7489 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
7590 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
91+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
92+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -2,26 +2,39 @@ github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAc
2 github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=2 github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
3 github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=3 github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
4 github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=4 github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
5+github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
5 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=6 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
6 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=8 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7-github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=9+github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
8-github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=10+github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
9-github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=11+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
10-github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=12+github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
13+github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
14+github.com/google/safehtml v0.1.0 h1:EwLKo8qawTKfsi0orxcQAZzu07cICaBeFMegAU9eaT8=
15+github.com/google/safehtml v0.1.0/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU=
11 github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4=16 github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4=
12 github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ=17 github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ=
13 github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=18 github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
14 github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=19 github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
15 github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=20 github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
21+github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
16 github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=22 github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
17 github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=23 github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
18 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=24 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
19 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=25 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
20 github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=26 github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
21 github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=27 github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
28+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
29+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
22 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=30 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
31+github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
32+github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c=
33+github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w=
23 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=34 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
24 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=35 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
36+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
37+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
25 github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=38 github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
26 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=39 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
27 github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=40 github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
@@ -66,6 +79,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
66 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=79 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
67 golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=80 golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
68 golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=81 golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
82+golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
83+golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
69 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=84 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
70 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=85 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
71 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=86 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
@@ -73,3 +88,5 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
73 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=88 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
74 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=89 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
75 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=90 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
91+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
92+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
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 {
1111 ParseError error
1212 }
1313
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) {
1716 if r.ParseError != nil {
1817 return "", r.ParseError
1918 }
@@ -23,10 +22,9 @@ func (r *Renderer) Render(data any) (string, error) {
2322 }
2423
2524 buf := new(bytes.Buffer)
26-
2725 err := r.Template.Execute(buf, data)
2826 if err != nil {
29- return "", errors.New("failed to execute template: " + err.Error())
27+ return "", err
3028 }
3129
3230 return buf.String(), nil
@@ -11,9 +11,8 @@ type Renderer struct {
11 ParseError error11 ParseError error
12 }12 }
13 13
14-// Render executes the template with the specified data as the dot object14+// 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.ParseError17 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(), nil30 return buf.String(), nil

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close