detached accf0b9

WIP render the /admin with the index.gohtml and the date from the strings.yml

commit
accf0b9b4aef26fc58bea137b00c45769ff9cffd
author
bnema · · 2023-09-13 18:39 UTC
parent
48af454
signature
unsigned
6 files · +123 -17
R internal/webui/strings.yml → internal/gotemplate/models/strings.yml +0 -0 renamed, 100% similar

renamed, 100% similar

A internal/gotemplate/render/html.go +51 -0
@@ -0,0 +1,51 @@
1+package render
2+
3+import (
4+ "bytes"
5+ "fmt"
6+ "html/template"
7+ "io/fs"
8+
9+ "github.com/bnema/gordon/internal/app"
10+)
11+
12+type Renderer struct {
13+ Template *template.Template
14+ ParseError error
15+}
16+
17+// Render function renders the template with the given data
18+func (r *Renderer) Render(data interface{}, a *app.App) (string, error) {
19+ if r.ParseError != nil {
20+ return "", fmt.Errorf("failed to parse template: %w", r.ParseError)
21+
22+ }
23+ if r.Template == nil {
24+ return "", fmt.Errorf("template is nil")
25+ }
26+
27+ buf := new(bytes.Buffer)
28+ err := r.Template.Execute(buf, data)
29+ if err != nil {
30+ return "", fmt.Errorf("failed to execute template: %w", err)
31+ }
32+
33+ return buf.String(), nil
34+}
35+
36+// GetRenderer function returns a new Renderer instance
37+func GetHTMLRenderer(filename string, fs fs.FS, a *app.App) (*Renderer, error) {
38+ // Check if the file exists in the provided fs.FS using fs.Open
39+ file, err := fs.Open(filename)
40+ if err != nil {
41+ return nil, fmt.Errorf("failed to open %s: %w", filename, err)
42+ }
43+ file.Close() // Close the file after checking
44+ tmpl, err := template.New(filename).ParseFS(fs, filename)
45+ if err != nil {
46+ return nil, fmt.Errorf("failed to parse %s: %w", filename, err)
47+ }
48+ return &Renderer{
49+ Template: tmpl,
50+ }, nil
51+}
@@ -0,0 +1,51 @@
1+package render
2+
3+import (
4+ "bytes"
5+ "fmt"
6+ "html/template"
7+ "io/fs"
8+
9+ "github.com/bnema/gordon/internal/app"
10+)
11+
12+type Renderer struct {
13+ Template *template.Template
14+ ParseError error
15+}
16+
17+// Render function renders the template with the given data
18+func (r *Renderer) Render(data interface{}, a *app.App) (string, error) {
19+ if r.ParseError != nil {
20+ return "", fmt.Errorf("failed to parse template: %w", r.ParseError)
21+
22+ }
23+ if r.Template == nil {
24+ return "", fmt.Errorf("template is nil")
25+ }
26+
27+ buf := new(bytes.Buffer)
28+ err := r.Template.Execute(buf, data)
29+ if err != nil {
30+ return "", fmt.Errorf("failed to execute template: %w", err)
31+ }
32+
33+ return buf.String(), nil
34+}
35+
36+// GetRenderer function returns a new Renderer instance
37+func GetHTMLRenderer(filename string, fs fs.FS, a *app.App) (*Renderer, error) {
38+ // Check if the file exists in the provided fs.FS using fs.Open
39+ file, err := fs.Open(filename)
40+ if err != nil {
41+ return nil, fmt.Errorf("failed to open %s: %w", filename, err)
42+ }
43+ file.Close() // Close the file after checking
44+ tmpl, err := template.New(filename).ParseFS(fs, filename)
45+ if err != nil {
46+ return nil, fmt.Errorf("failed to parse %s: %w", filename, err)
47+ }
48+ return &Renderer{
49+ Template: tmpl,
50+ }, nil
51+}
M internal/httpserve/handler/adminroutes.go +30 -3
@@ -1,10 +1,37 @@
11 package handler
22
33 import (
4+ "fmt"
5+
6+ "github.com/bnema/gordon/internal/app"
7+ "github.com/bnema/gordon/internal/gotemplate/render"
8+ "github.com/bnema/gordon/internal/httpserve/middleware"
9+ "github.com/bnema/gordon/internal/webui"
410 "github.com/labstack/echo/v4"
511 )
612
7-// Handle the admin route to display index.gohtml from the templateFS with the data from strings.yaml
8-func AdminRoute(c echo.Context) error {
9- return c.Render(200, "index.gohtml", c.Get("data"))
13+// Handle the admin route to display index.gohtml from the templateFS with the data from strings.yml
14+func AdminRoute(c echo.Context, a *app.App) error {
15+ lang := c.Get(middleware.LangKey).(string)
16+ fmt.Println(lang)
17+ data := webui.StringsYamlData{}
18+ err := webui.ReadStringsDataFromYAML(lang, a.TemplateFS, "strings.yml", &data)
19+ if err != nil {
20+ return fmt.Errorf("failed to read strings data from YAML: %w", err)
21+ }
22+
23+ // Navigate inside the fs.FS to get the template
24+
25+ rendererData, err := render.GetHTMLRenderer("html/admin/index.gohtml", a.TemplateFS, a)
26+ if err != nil {
27+ return fmt.Errorf("failed to get renderer: %w", err)
28+ }
29+
30+ renderedHTML, err := rendererData.Render(data, a)
31+ fmt.Println(renderedHTML)
32+ if err != nil {
33+ return fmt.Errorf("failed to render template: %w", err)
34+ }
35+
36+ return c.HTML(200, renderedHTML)
1037 }
@@ -1,10 +1,37 @@
1 package handler1 package handler
2 2
3 import (3 import (
4+ "fmt"
5+
6+ "github.com/bnema/gordon/internal/app"
7+ "github.com/bnema/gordon/internal/gotemplate/render"
8+ "github.com/bnema/gordon/internal/httpserve/middleware"
9+ "github.com/bnema/gordon/internal/webui"
4 "github.com/labstack/echo/v4"10 "github.com/labstack/echo/v4"
5 )11 )
6 12
7-// Handle the admin route to display index.gohtml from the templateFS with the data from strings.yaml13+// Handle the admin route to display index.gohtml from the templateFS with the data from strings.yml
8-func AdminRoute(c echo.Context) error {14+func AdminRoute(c echo.Context, a *app.App) error {
9- return c.Render(200, "index.gohtml", c.Get("data"))15+ lang := c.Get(middleware.LangKey).(string)
16+ fmt.Println(lang)
17+ data := webui.StringsYamlData{}
18+ err := webui.ReadStringsDataFromYAML(lang, a.TemplateFS, "strings.yml", &data)
19+ if err != nil {
20+ return fmt.Errorf("failed to read strings data from YAML: %w", err)
21+ }
22+
23+ // Navigate inside the fs.FS to get the template
24+
25+ rendererData, err := render.GetHTMLRenderer("html/admin/index.gohtml", a.TemplateFS, a)
26+ if err != nil {
27+ return fmt.Errorf("failed to get renderer: %w", err)
28+ }
29+
30+ renderedHTML, err := rendererData.Render(data, a)
31+ fmt.Println(renderedHTML)
32+ if err != nil {
33+ return fmt.Errorf("failed to render template: %w", err)
34+ }
35+
36+ return c.HTML(200, renderedHTML)
10 }37 }
A internal/httpserve/middleware/error.go +22 -0
@@ -0,0 +1,22 @@
1+package middleware
2+
3+import (
4+ "log"
5+ "net/http"
6+
7+ "github.com/labstack/echo/v4"
8+)
9+
10+func ErrorHandler(next echo.HandlerFunc) echo.HandlerFunc {
11+ return func(c echo.Context) error {
12+ err := next(c)
13+ if err != nil {
14+ log.Println("Error encountered:", err)
15+ if he, ok := err.(*echo.HTTPError); ok {
16+ return c.String(he.Code, he.Message.(string))
17+ }
18+ return c.String(http.StatusInternalServerError, "Internal Server Error")
19+ }
20+ return nil
21+ }
22+}
@@ -0,0 +1,22 @@
1+package middleware
2+
3+import (
4+ "log"
5+ "net/http"
6+
7+ "github.com/labstack/echo/v4"
8+)
9+
10+func ErrorHandler(next echo.HandlerFunc) echo.HandlerFunc {
11+ return func(c echo.Context) error {
12+ err := next(c)
13+ if err != nil {
14+ log.Println("Error encountered:", err)
15+ if he, ok := err.(*echo.HTTPError); ok {
16+ return c.String(he.Code, he.Message.(string))
17+ }
18+ return c.String(http.StatusInternalServerError, "Internal Server Error")
19+ }
20+ return nil
21+ }
22+}
M internal/httpserve/router.go +9 -3
@@ -1,8 +1,6 @@
11 package httpserve
22
33 import (
4- "fmt"
5-
64 "github.com/bnema/gordon/internal/app"
75 "github.com/bnema/gordon/internal/httpserve/handler"
86 "github.com/bnema/gordon/internal/httpserve/middleware"
@@ -12,7 +10,7 @@ import (
1210 // RegisterRoutes registers all routes for the application
1311 func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo {
1412 AdminPath := a.AdminPath
15- fmt.Println(AdminPath)
13+ e.Use(middleware.ErrorHandler)
1614 // Use middlewares
1715 // e.Use(middleware.SecureRoutes())
1816 e.Use(middleware.ColorSchemeDetection)
@@ -20,6 +18,8 @@ func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo {
2018
2119 // Register routes
2220
21+ // Serve admin routes
22+ bindAdminRoute(e, a, AdminPath)
2323 // Serve static files
2424 bindStaticRoute(e, a, "/*")
2525 // Protect the root path with a 403
@@ -35,3 +35,9 @@ func bindStaticRoute(e *echo.Echo, a *app.App, path string) {
3535 return handler.StaticRoute(c, a)
3636 })
3737 }
38+
39+func bindAdminRoute(e *echo.Echo, a *app.App, path string) {
40+ e.GET(path, func(c echo.Context) error {
41+ return handler.AdminRoute(c, a)
42+ })
43+}
@@ -1,8 +1,6 @@
1 package httpserve1 package httpserve
2 2
3 import (3 import (
4- "fmt"
5-
6 "github.com/bnema/gordon/internal/app"4 "github.com/bnema/gordon/internal/app"
7 "github.com/bnema/gordon/internal/httpserve/handler"5 "github.com/bnema/gordon/internal/httpserve/handler"
8 "github.com/bnema/gordon/internal/httpserve/middleware"6 "github.com/bnema/gordon/internal/httpserve/middleware"
@@ -12,7 +10,7 @@ import (
12 // RegisterRoutes registers all routes for the application10 // RegisterRoutes registers all routes for the application
13 func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo {11 func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo {
14 AdminPath := a.AdminPath12 AdminPath := a.AdminPath
15- fmt.Println(AdminPath)13+ e.Use(middleware.ErrorHandler)
16 // Use middlewares14 // Use middlewares
17 // e.Use(middleware.SecureRoutes())15 // e.Use(middleware.SecureRoutes())
18 e.Use(middleware.ColorSchemeDetection)16 e.Use(middleware.ColorSchemeDetection)
@@ -20,6 +18,8 @@ func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo {
20 18
21 // Register routes19 // Register routes
22 20
21+ // Serve admin routes
22+ bindAdminRoute(e, a, AdminPath)
23 // Serve static files23 // Serve static files
24 bindStaticRoute(e, a, "/*")24 bindStaticRoute(e, a, "/*")
25 // Protect the root path with a 40325 // Protect the root path with a 403
@@ -35,3 +35,9 @@ func bindStaticRoute(e *echo.Echo, a *app.App, path string) {
35 return handler.StaticRoute(c, a)35 return handler.StaticRoute(c, a)
36 })36 })
37 }37 }
38+
39+func bindAdminRoute(e *echo.Echo, a *app.App, path string) {
40+ e.GET(path, func(c echo.Context) error {
41+ return handler.AdminRoute(c, a)
42+ })
43+}
M pkg/utils/htmx/htmlfragments.go +11 -11
@@ -2,41 +2,41 @@ package htmx
22
33 import (
44 "bytes"
5+ "fmt"
56
67 "github.com/PuerkitoBio/goquery"
7-
8+ "github.com/bnema/gordon/internal/app"
9+ "github.com/bnema/gordon/internal/gotemplate/render"
810 )
911
1012 // GetHTMLFragmentByID returns the HTML fragment with the specified id
11-func GetHTMLFragmentByID(id string, data interface{}, a *app.App, ac *app.Config) (string, error) {
13+func GetHTMLFragmentByID(id string, data interface{}, a *app.App) (string, error) {
1214 // 1. Render the template
13- renderer, err := templating.GetHTMLRenderer("components.gohtml", ac.GetTemplateFS(), a)
15+ renderer, err := render.GetHTMLRenderer("components.gohtml", a.TemplateFS, a)
1416 if err != nil {
15- return "", err
17+ return "", fmt.Errorf("failed to get renderer: %w", err)
1618 }
1719
1820 renderedHTML, err := renderer.Render(data, a)
1921 if err != nil {
20- return "", err
22+ return "", fmt.Errorf("failed to render template: %w", err)
2123 }
2224
2325 // 2. Parse the rendered HTML with goquery
2426 doc, err := goquery.NewDocumentFromReader(bytes.NewReader([]byte(renderedHTML)))
2527 if err != nil {
26- return "", err
28+ return "", fmt.Errorf("failed to parse rendered HTML: %w", err)
2729 }
2830
2931 // 3. Find the div with the specified id
3032 fragmentContent, err := doc.Find("#" + id).Html()
3133 if err != nil {
32- return "", err
34+ return "", fmt.Errorf("failed to find fragment with id %s: %w", id, err)
3335 }
3436
35- if fragmentcontent == "" {
36- return "", err
37+ if fragmentContent == "" {
38+ return "", fmt.Errorf("failed to find fragment with id %s: %w", id, err)
3739 }
3840
3941 return fragmentContent, nil
4042 }
41-
42-
@@ -2,41 +2,41 @@ package htmx
2 2
3 import (3 import (
4 "bytes"4 "bytes"
5+ "fmt"
5 6
6 "github.com/PuerkitoBio/goquery"7 "github.com/PuerkitoBio/goquery"
7-8+ "github.com/bnema/gordon/internal/app"
9+ "github.com/bnema/gordon/internal/gotemplate/render"
8 )10 )
9 11
10 // GetHTMLFragmentByID returns the HTML fragment with the specified id12 // GetHTMLFragmentByID returns the HTML fragment with the specified id
11-func GetHTMLFragmentByID(id string, data interface{}, a *app.App, ac *app.Config) (string, error) {13+func GetHTMLFragmentByID(id string, data interface{}, a *app.App) (string, error) {
12 // 1. Render the template14 // 1. Render the template
13- renderer, err := templating.GetHTMLRenderer("components.gohtml", ac.GetTemplateFS(), a)15+ renderer, err := render.GetHTMLRenderer("components.gohtml", a.TemplateFS, a)
14 if err != nil {16 if err != nil {
15- return "", err17+ return "", fmt.Errorf("failed to get renderer: %w", err)
16 }18 }
17 19
18 renderedHTML, err := renderer.Render(data, a)20 renderedHTML, err := renderer.Render(data, a)
19 if err != nil {21 if err != nil {
20- return "", err22+ return "", fmt.Errorf("failed to render template: %w", err)
21 }23 }
22 24
23 // 2. Parse the rendered HTML with goquery25 // 2. Parse the rendered HTML with goquery
24 doc, err := goquery.NewDocumentFromReader(bytes.NewReader([]byte(renderedHTML)))26 doc, err := goquery.NewDocumentFromReader(bytes.NewReader([]byte(renderedHTML)))
25 if err != nil {27 if err != nil {
26- return "", err28+ return "", fmt.Errorf("failed to parse rendered HTML: %w", err)
27 }29 }
28 30
29 // 3. Find the div with the specified id31 // 3. Find the div with the specified id
30 fragmentContent, err := doc.Find("#" + id).Html()32 fragmentContent, err := doc.Find("#" + id).Html()
31 if err != nil {33 if err != nil {
32- return "", err34+ return "", fmt.Errorf("failed to find fragment with id %s: %w", id, err)
33 }35 }
34 36
35- if fragmentcontent == "" {37+ if fragmentContent == "" {
36- return "", err38+ return "", fmt.Errorf("failed to find fragment with id %s: %w", id, err)
37 }39 }
38 40
39 return fragmentContent, nil41 return fragmentContent, nil
40 }42 }
41-
42-

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