StaticFS served on the website root path
- commit
- 8ca5040edc4c838748304be449c710456a23ca79
- author
- bnema · · 2023-09-13 17:06 UTC
- parent
- b0b2bc7
- signature
- unsigned
14 files · +247 -7
M go.mod +7 -0
| @@ -4,11 +4,17 @@ go 1.21.1 | |||
| 4 | 4 | ||
| 5 | 5 | require ( | |
| 6 | 6 | github.com/PuerkitoBio/goquery v1.8.1 | |
| 7 | + | github.com/labstack/echo-contrib v0.15.0 | |
| 7 | 8 | github.com/labstack/echo/v4 v4.11.1 | |
| 9 | + | gopkg.in/yaml.v3 v3.0.1 | |
| 8 | 10 | ) | |
| 9 | 11 | ||
| 10 | 12 | require ( | |
| 11 | 13 | github.com/andybalholm/cascadia v1.3.1 // indirect | |
| 14 | + | github.com/golang-jwt/jwt v3.2.2+incompatible // indirect | |
| 15 | + | github.com/gorilla/context v1.1.1 // indirect | |
| 16 | + | github.com/gorilla/securecookie v1.1.1 // indirect | |
| 17 | + | github.com/gorilla/sessions v1.2.1 // indirect | |
| 12 | 18 | github.com/labstack/gommon v0.4.0 // indirect | |
| 13 | 19 | github.com/mattn/go-colorable v0.1.13 // indirect | |
| 14 | 20 | github.com/mattn/go-isatty v0.0.19 // indirect | |
| @@ -18,4 +24,5 @@ require ( | |||
| 18 | 24 | golang.org/x/net v0.12.0 // indirect | |
| 19 | 25 | golang.org/x/sys v0.10.0 // indirect | |
| 20 | 26 | golang.org/x/text v0.11.0 // indirect | |
| 27 | + | golang.org/x/time v0.3.0 // indirect | |
| 21 | 28 | ) | |
| @@ -4,11 +4,17 @@ go 1.21.1 | |||||
| 4 | 4 | ||||
| 5 | require ( | 5 | require ( | ||
| 6 | github.com/PuerkitoBio/goquery v1.8.1 | 6 | github.com/PuerkitoBio/goquery v1.8.1 | ||
| 7 | + | github.com/labstack/echo-contrib v0.15.0 | |||
| 7 | github.com/labstack/echo/v4 v4.11.1 | 8 | github.com/labstack/echo/v4 v4.11.1 | ||
| 9 | + | gopkg.in/yaml.v3 v3.0.1 | |||
| 8 | ) | 10 | ) | ||
| 9 | 11 | ||||
| 10 | require ( | 12 | require ( | ||
| 11 | github.com/andybalholm/cascadia v1.3.1 // indirect | 13 | github.com/andybalholm/cascadia v1.3.1 // indirect | ||
| 14 | + | github.com/golang-jwt/jwt v3.2.2+incompatible // indirect | |||
| 15 | + | github.com/gorilla/context v1.1.1 // indirect | |||
| 16 | + | github.com/gorilla/securecookie v1.1.1 // indirect | |||
| 17 | + | github.com/gorilla/sessions v1.2.1 // indirect | |||
| 12 | github.com/labstack/gommon v0.4.0 // indirect | 18 | github.com/labstack/gommon v0.4.0 // indirect | ||
| 13 | github.com/mattn/go-colorable v0.1.13 // indirect | 19 | github.com/mattn/go-colorable v0.1.13 // indirect | ||
| 14 | github.com/mattn/go-isatty v0.0.19 // indirect | 20 | github.com/mattn/go-isatty v0.0.19 // indirect | ||
| @@ -18,4 +24,5 @@ require ( | |||||
| 18 | golang.org/x/net v0.12.0 // indirect | 24 | golang.org/x/net v0.12.0 // indirect | ||
| 19 | golang.org/x/sys v0.10.0 // indirect | 25 | golang.org/x/sys v0.10.0 // indirect | ||
| 20 | golang.org/x/text v0.11.0 // indirect | 26 | golang.org/x/text v0.11.0 // indirect | ||
| 27 | + | golang.org/x/time v0.3.0 // indirect | |||
| 21 | ) | 28 | ) | ||
M go.sum +13 -0
| @@ -5,6 +5,16 @@ github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEq | |||
| 5 | 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | |
| 6 | 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | |
| 7 | 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | |
| 8 | + | github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= | |
| 9 | + | github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= | |
| 10 | + | github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= | |
| 11 | + | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= | |
| 12 | + | github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= | |
| 13 | + | github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= | |
| 14 | + | github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= | |
| 15 | + | github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= | |
| 16 | + | github.com/labstack/echo-contrib v0.15.0 h1:9K+oRU265y4Mu9zpRDv3X+DGTqUALY6oRHCSZZKCRVU= | |
| 17 | + | github.com/labstack/echo-contrib v0.15.0/go.mod h1:lei+qt5CLB4oa7VHTE0yEfQSEB9XTJI1LUqko9UWvo4= | |
| 8 | 18 | github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4= | |
| 9 | 19 | github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ= | |
| 10 | 20 | github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= | |
| @@ -66,10 +76,13 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | |||
| 66 | 76 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | |
| 67 | 77 | golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= | |
| 68 | 78 | golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= | |
| 79 | + | golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= | |
| 80 | + | golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | |
| 69 | 81 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | |
| 70 | 82 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | |
| 71 | 83 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= | |
| 72 | 84 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | |
| 85 | + | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | |
| 73 | 86 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | |
| 74 | 87 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | |
| 75 | 88 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | |
| @@ -5,6 +5,16 @@ github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEq | |||||
| 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
| 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
| 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
| 8 | + | github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= | |||
| 9 | + | github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= | |||
| 10 | + | github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= | |||
| 11 | + | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= | |||
| 12 | + | github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= | |||
| 13 | + | github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= | |||
| 14 | + | github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= | |||
| 15 | + | github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= | |||
| 16 | + | github.com/labstack/echo-contrib v0.15.0 h1:9K+oRU265y4Mu9zpRDv3X+DGTqUALY6oRHCSZZKCRVU= | |||
| 17 | + | github.com/labstack/echo-contrib v0.15.0/go.mod h1:lei+qt5CLB4oa7VHTE0yEfQSEB9XTJI1LUqko9UWvo4= | |||
| 8 | github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4= | 18 | github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4= | ||
| 9 | github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ= | 19 | github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ= | ||
| 10 | github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= | 20 | github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= | ||
| @@ -66,10 +76,13 @@ 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= | 76 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | ||
| 67 | golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= | 77 | 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= | 78 | golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= | ||
| 79 | + | golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= | |||
| 80 | + | 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= | 81 | 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= | 82 | 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= | 83 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= | ||
| 72 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | 84 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
| 85 | + | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | |||
| 73 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | 86 | 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= | 87 | 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= | 88 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||
M internal/httpserve/handler/adminroutes.go +8 -3
| @@ -1,5 +1,10 @@ | |||
| 1 | - | package handlers | |
| 2 | - | ||
| 3 | - | import () | |
| 1 | + | package handler | |
| 4 | 2 | ||
| 3 | + | import ( | |
| 4 | + | "github.com/labstack/echo/v4" | |
| 5 | + | ) | |
| 5 | 6 | ||
| 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")) | |
| 10 | + | } | |
| @@ -1,5 +1,10 @@ | |||||
| 1 | - | package handlers | 1 | + | package handler |
| 2 | - | ||||
| 3 | - | import () | |||
| 4 | 2 | ||||
| 3 | + | import ( | |||
| 4 | + | "github.com/labstack/echo/v4" | |||
| 5 | + | ) | |||
| 5 | 6 | ||||
| 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")) | |||
| 10 | + | } | |||
M internal/httpserve/handler/htmx.go +1 -0
| @@ -0,0 +1,1 @@ | |||
| 1 | + | package handler | |
| @@ -0,0 +1,1 @@ | |||||
| 1 | + | package handler | |||
M internal/httpserve/handler/static.go +24 -0
| @@ -0,0 +1,24 @@ | |||
| 1 | + | package handler | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "net/http" | |
| 5 | + | "os" | |
| 6 | + | ||
| 7 | + | "github.com/bnema/gordon/internal/app" | |
| 8 | + | "github.com/labstack/echo/v4" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | // StaticRoute serves static files from the embedded filesystem | |
| 12 | + | func StaticRoute(c echo.Context, a *app.App) error { | |
| 13 | + | // Set the cache-control header based on PROD environment variable | |
| 14 | + | if os.Getenv("PROD") == "true" { | |
| 15 | + | c.Response().Header().Set("Cache-Control", "public, max-age=86400") | |
| 16 | + | } else { | |
| 17 | + | c.Response().Header().Set("Cache-Control", "no-cache") | |
| 18 | + | } | |
| 19 | + | ||
| 20 | + | // Serve the file from the embedded filesystem | |
| 21 | + | publicFS := http.FileServer(http.FS(a.PublicFS)) | |
| 22 | + | publicFS.ServeHTTP(c.Response(), c.Request()) | |
| 23 | + | return nil | |
| 24 | + | } | |
| @@ -0,0 +1,24 @@ | |||||
| 1 | + | package handler | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "net/http" | |||
| 5 | + | "os" | |||
| 6 | + | ||||
| 7 | + | "github.com/bnema/gordon/internal/app" | |||
| 8 | + | "github.com/labstack/echo/v4" | |||
| 9 | + | ) | |||
| 10 | + | ||||
| 11 | + | // StaticRoute serves static files from the embedded filesystem | |||
| 12 | + | func StaticRoute(c echo.Context, a *app.App) error { | |||
| 13 | + | // Set the cache-control header based on PROD environment variable | |||
| 14 | + | if os.Getenv("PROD") == "true" { | |||
| 15 | + | c.Response().Header().Set("Cache-Control", "public, max-age=86400") | |||
| 16 | + | } else { | |||
| 17 | + | c.Response().Header().Set("Cache-Control", "no-cache") | |||
| 18 | + | } | |||
| 19 | + | ||||
| 20 | + | // Serve the file from the embedded filesystem | |||
| 21 | + | publicFS := http.FileServer(http.FS(a.PublicFS)) | |||
| 22 | + | publicFS.ServeHTTP(c.Response(), c.Request()) | |||
| 23 | + | return nil | |||
| 24 | + | } | |||
A internal/httpserve/middleware/colorschemedetect.go +35 -0
| @@ -0,0 +1,35 @@ | |||
| 1 | + | package middleware | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "github.com/labstack/echo-contrib/session" | |
| 5 | + | "github.com/labstack/echo/v4" | |
| 6 | + | ) | |
| 7 | + | ||
| 8 | + | // ColorSchemeDetection detects the current color scheme and sets it in the context | |
| 9 | + | func ColorSchemeDetection(next echo.HandlerFunc) echo.HandlerFunc { | |
| 10 | + | ColorSchemeKey := "CurrentColorScheme" | |
| 11 | + | return func(c echo.Context) error { | |
| 12 | + | colorScheme := detectCurrentColorScheme(c) | |
| 13 | + | c.Set(ColorSchemeKey, colorScheme) | |
| 14 | + | return next(c) | |
| 15 | + | } | |
| 16 | + | } | |
| 17 | + | ||
| 18 | + | func detectCurrentColorScheme(c echo.Context) string { | |
| 19 | + | // Check if the color scheme key is set in the session storage | |
| 20 | + | sess, err := session.Get("session", c) | |
| 21 | + | if err != nil { | |
| 22 | + | return "light" | |
| 23 | + | } | |
| 24 | + | ||
| 25 | + | if colorSchemeValue, ok := sess.Values["colorScheme"]; ok && colorSchemeValue != nil { | |
| 26 | + | return colorSchemeValue.(string) // Return the color scheme value from the session storage | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | // If color scheme key is not set, proceed with "Sec-CH-Prefers-Color-Scheme" header detection | |
| 30 | + | header := c.Request().Header.Get("Sec-CH-Prefers-Color-Scheme") | |
| 31 | + | if header == "dark" { | |
| 32 | + | return "dark" | |
| 33 | + | } | |
| 34 | + | return "light" // Default to light mode | |
| 35 | + | } | |
| @@ -0,0 +1,35 @@ | |||||
| 1 | + | package middleware | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "github.com/labstack/echo-contrib/session" | |||
| 5 | + | "github.com/labstack/echo/v4" | |||
| 6 | + | ) | |||
| 7 | + | ||||
| 8 | + | // ColorSchemeDetection detects the current color scheme and sets it in the context | |||
| 9 | + | func ColorSchemeDetection(next echo.HandlerFunc) echo.HandlerFunc { | |||
| 10 | + | ColorSchemeKey := "CurrentColorScheme" | |||
| 11 | + | return func(c echo.Context) error { | |||
| 12 | + | colorScheme := detectCurrentColorScheme(c) | |||
| 13 | + | c.Set(ColorSchemeKey, colorScheme) | |||
| 14 | + | return next(c) | |||
| 15 | + | } | |||
| 16 | + | } | |||
| 17 | + | ||||
| 18 | + | func detectCurrentColorScheme(c echo.Context) string { | |||
| 19 | + | // Check if the color scheme key is set in the session storage | |||
| 20 | + | sess, err := session.Get("session", c) | |||
| 21 | + | if err != nil { | |||
| 22 | + | return "light" | |||
| 23 | + | } | |||
| 24 | + | ||||
| 25 | + | if colorSchemeValue, ok := sess.Values["colorScheme"]; ok && colorSchemeValue != nil { | |||
| 26 | + | return colorSchemeValue.(string) // Return the color scheme value from the session storage | |||
| 27 | + | } | |||
| 28 | + | ||||
| 29 | + | // If color scheme key is not set, proceed with "Sec-CH-Prefers-Color-Scheme" header detection | |||
| 30 | + | header := c.Request().Header.Get("Sec-CH-Prefers-Color-Scheme") | |||
| 31 | + | if header == "dark" { | |||
| 32 | + | return "dark" | |||
| 33 | + | } | |||
| 34 | + | return "light" // Default to light mode | |||
| 35 | + | } | |||
D internal/httpserve/middleware/detectcolorscheme.go +0 -0
no textual change.
D internal/httpserve/middleware/detectlang.go +0 -0
no textual change.
A internal/httpserve/middleware/langdetect.go +40 -0
| @@ -0,0 +1,40 @@ | |||
| 1 | + | package middleware | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "strings" | |
| 5 | + | ||
| 6 | + | "github.com/labstack/echo-contrib/session" | |
| 7 | + | "github.com/labstack/echo/v4" | |
| 8 | + | ) | |
| 9 | + | ||
| 10 | + | // LangKey is a key for setting and getting the language from the context | |
| 11 | + | const LangKey = "CurrentLang" | |
| 12 | + | ||
| 13 | + | // LanguageDetectiondetects the current language and sets it in the context | |
| 14 | + | func LanguageDetection(next echo.HandlerFunc) echo.HandlerFunc { | |
| 15 | + | return func(c echo.Context) error { | |
| 16 | + | lang := detectCurrentLanguage(c) | |
| 17 | + | c.Set(LangKey, lang) | |
| 18 | + | return next(c) | |
| 19 | + | } | |
| 20 | + | } | |
| 21 | + | ||
| 22 | + | func detectCurrentLanguage(c echo.Context) string { | |
| 23 | + | // Check if the lang key is set in the session storage | |
| 24 | + | sess, err := session.Get("session", c) | |
| 25 | + | if err != nil { | |
| 26 | + | // If session storage is not available, proceed with "Accept-Language" header detection | |
| 27 | + | return "en" | |
| 28 | + | } | |
| 29 | + | ||
| 30 | + | if langValue, ok := sess.Values["lang"]; ok && langValue != nil { | |
| 31 | + | return langValue.(string) // Return the lang value from the session storage | |
| 32 | + | } | |
| 33 | + | ||
| 34 | + | // If lang key is not set, proceed with "Accept-Language" header detection | |
| 35 | + | header := c.Request().Header.Get("Accept-Language") | |
| 36 | + | if strings.Contains(header, "fr") { | |
| 37 | + | return "fr" | |
| 38 | + | } | |
| 39 | + | return "en" // Default to English | |
| 40 | + | } | |
| @@ -0,0 +1,40 @@ | |||||
| 1 | + | package middleware | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "strings" | |||
| 5 | + | ||||
| 6 | + | "github.com/labstack/echo-contrib/session" | |||
| 7 | + | "github.com/labstack/echo/v4" | |||
| 8 | + | ) | |||
| 9 | + | ||||
| 10 | + | // LangKey is a key for setting and getting the language from the context | |||
| 11 | + | const LangKey = "CurrentLang" | |||
| 12 | + | ||||
| 13 | + | // LanguageDetectiondetects the current language and sets it in the context | |||
| 14 | + | func LanguageDetection(next echo.HandlerFunc) echo.HandlerFunc { | |||
| 15 | + | return func(c echo.Context) error { | |||
| 16 | + | lang := detectCurrentLanguage(c) | |||
| 17 | + | c.Set(LangKey, lang) | |||
| 18 | + | return next(c) | |||
| 19 | + | } | |||
| 20 | + | } | |||
| 21 | + | ||||
| 22 | + | func detectCurrentLanguage(c echo.Context) string { | |||
| 23 | + | // Check if the lang key is set in the session storage | |||
| 24 | + | sess, err := session.Get("session", c) | |||
| 25 | + | if err != nil { | |||
| 26 | + | // If session storage is not available, proceed with "Accept-Language" header detection | |||
| 27 | + | return "en" | |||
| 28 | + | } | |||
| 29 | + | ||||
| 30 | + | if langValue, ok := sess.Values["lang"]; ok && langValue != nil { | |||
| 31 | + | return langValue.(string) // Return the lang value from the session storage | |||
| 32 | + | } | |||
| 33 | + | ||||
| 34 | + | // If lang key is not set, proceed with "Accept-Language" header detection | |||
| 35 | + | header := c.Request().Header.Get("Accept-Language") | |||
| 36 | + | if strings.Contains(header, "fr") { | |||
| 37 | + | return "fr" | |||
| 38 | + | } | |||
| 39 | + | return "en" // Default to English | |||
| 40 | + | } | |||
M internal/httpserve/middleware/secure.go +17 -0
| @@ -0,0 +1,17 @@ | |||
| 1 | + | package middleware | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "github.com/labstack/echo/v4" | |
| 5 | + | "github.com/labstack/echo/v4/middleware" | |
| 6 | + | ) | |
| 7 | + | ||
| 8 | + | func SecureRoutes() echo.MiddlewareFunc { | |
| 9 | + | return middleware.SecureWithConfig(middleware.SecureConfig{ | |
| 10 | + | XSSProtection: "1; mode=block", | |
| 11 | + | ContentTypeNosniff: "nosniff", | |
| 12 | + | XFrameOptions: "SAMEORIGIN", | |
| 13 | + | HSTSMaxAge: 3600, | |
| 14 | + | HSTSExcludeSubdomains: false, | |
| 15 | + | ContentSecurityPolicy: "default-src 'self'", | |
| 16 | + | }) | |
| 17 | + | } | |
| @@ -0,0 +1,17 @@ | |||||
| 1 | + | package middleware | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "github.com/labstack/echo/v4" | |||
| 5 | + | "github.com/labstack/echo/v4/middleware" | |||
| 6 | + | ) | |||
| 7 | + | ||||
| 8 | + | func SecureRoutes() echo.MiddlewareFunc { | |||
| 9 | + | return middleware.SecureWithConfig(middleware.SecureConfig{ | |||
| 10 | + | XSSProtection: "1; mode=block", | |||
| 11 | + | ContentTypeNosniff: "nosniff", | |||
| 12 | + | XFrameOptions: "SAMEORIGIN", | |||
| 13 | + | HSTSMaxAge: 3600, | |||
| 14 | + | HSTSExcludeSubdomains: false, | |||
| 15 | + | ContentSecurityPolicy: "default-src 'self'", | |||
| 16 | + | }) | |||
| 17 | + | } | |||
M internal/httpserve/router.go +24 -4
| @@ -1,17 +1,37 @@ | |||
| 1 | 1 | package httpserve | |
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | + | "fmt" | |
| 5 | + | ||
| 4 | 6 | "github.com/bnema/gordon/internal/app" | |
| 7 | + | "github.com/bnema/gordon/internal/httpserve/handler" | |
| 8 | + | "github.com/bnema/gordon/internal/httpserve/middleware" | |
| 5 | 9 | "github.com/labstack/echo/v4" | |
| 6 | 10 | ) | |
| 7 | 11 | ||
| 12 | + | // RegisterRoutes registers all routes for the application | |
| 8 | 13 | func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo { | |
| 9 | 14 | AdminPath := a.AdminPath | |
| 10 | - | // Hello world on the admin path | |
| 11 | - | e.GET(AdminPath, helloWorld) | |
| 15 | + | fmt.Println(AdminPath) | |
| 16 | + | // Use middlewares | |
| 17 | + | // e.Use(middleware.SecureRoutes()) | |
| 18 | + | e.Use(middleware.ColorSchemeDetection) | |
| 19 | + | e.Use(middleware.LanguageDetection) | |
| 20 | + | ||
| 21 | + | // Register routes | |
| 22 | + | ||
| 23 | + | // Serve static files | |
| 24 | + | bindStaticRoute(e, a, "/*") | |
| 25 | + | // Protect the root path with a 403 | |
| 26 | + | e.GET("/", func(c echo.Context) error { | |
| 27 | + | return c.String(403, "403 Forbidden") | |
| 28 | + | }) | |
| 29 | + | ||
| 12 | 30 | return e | |
| 13 | 31 | } | |
| 14 | 32 | ||
| 15 | - | func helloWorld(c echo.Context) error { | |
| 16 | - | return c.String(200, "Hello world") | |
| 33 | + | func bindStaticRoute(e *echo.Echo, a *app.App, path string) { | |
| 34 | + | e.GET(path, func(c echo.Context) error { | |
| 35 | + | return handler.StaticRoute(c, a) | |
| 36 | + | }) | |
| 17 | 37 | } | |
| @@ -1,17 +1,37 @@ | |||||
| 1 | package httpserve | 1 | package httpserve | ||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | + | "fmt" | |||
| 5 | + | ||||
| 4 | "github.com/bnema/gordon/internal/app" | 6 | "github.com/bnema/gordon/internal/app" | ||
| 7 | + | "github.com/bnema/gordon/internal/httpserve/handler" | |||
| 8 | + | "github.com/bnema/gordon/internal/httpserve/middleware" | |||
| 5 | "github.com/labstack/echo/v4" | 9 | "github.com/labstack/echo/v4" | ||
| 6 | ) | 10 | ) | ||
| 7 | 11 | ||||
| 12 | + | // RegisterRoutes registers all routes for the application | |||
| 8 | func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo { | 13 | func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo { | ||
| 9 | AdminPath := a.AdminPath | 14 | AdminPath := a.AdminPath | ||
| 10 | - | // Hello world on the admin path | 15 | + | fmt.Println(AdminPath) |
| 11 | - | e.GET(AdminPath, helloWorld) | 16 | + | // Use middlewares |
| 17 | + | // e.Use(middleware.SecureRoutes()) | |||
| 18 | + | e.Use(middleware.ColorSchemeDetection) | |||
| 19 | + | e.Use(middleware.LanguageDetection) | |||
| 20 | + | ||||
| 21 | + | // Register routes | |||
| 22 | + | ||||
| 23 | + | // Serve static files | |||
| 24 | + | bindStaticRoute(e, a, "/*") | |||
| 25 | + | // Protect the root path with a 403 | |||
| 26 | + | e.GET("/", func(c echo.Context) error { | |||
| 27 | + | return c.String(403, "403 Forbidden") | |||
| 28 | + | }) | |||
| 29 | + | ||||
| 12 | return e | 30 | return e | ||
| 13 | } | 31 | } | ||
| 14 | 32 | ||||
| 15 | - | func helloWorld(c echo.Context) error { | 33 | + | func bindStaticRoute(e *echo.Echo, a *app.App, path string) { |
| 16 | - | return c.String(200, "Hello world") | 34 | + | e.GET(path, func(c echo.Context) error { |
| 35 | + | return handler.StaticRoute(c, a) | |||
| 36 | + | }) | |||
| 17 | } | 37 | } | ||
A internal/webui/strings.go +46 -0
| @@ -0,0 +1,46 @@ | |||
| 1 | + | package webui | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "fmt" | |
| 5 | + | "io/fs" | |
| 6 | + | ||
| 7 | + | "github.com/bnema/gordon/pkg/utils/parser" | |
| 8 | + | ) | |
| 9 | + | ||
| 10 | + | type LangData map[string]interface{} | |
| 11 | + | ||
| 12 | + | type StringsModel struct { | |
| 13 | + | Header string `yaml:"header"` | |
| 14 | + | Subheader string `yaml:"subheader"` | |
| 15 | + | } | |
| 16 | + | type StringsYamlData struct { | |
| 17 | + | StringsModel StringsModel `yaml:"strings_model"` | |
| 18 | + | EN LangData `yaml:"en"` | |
| 19 | + | FR LangData `yaml:"fr"` | |
| 20 | + | CurrentLang LangData | |
| 21 | + | } | |
| 22 | + | ||
| 23 | + | // ReadStringsDataFromYAML reads the strings.yaml file and unmarshals it into the out interface | |
| 24 | + | func ReadStringsDataFromYAML(lang string, fs fs.FS, filename string, out interface{}) error { | |
| 25 | + | ||
| 26 | + | err := parser.OpenYamlFile(fs, filename, out) | |
| 27 | + | if err != nil { | |
| 28 | + | return err | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | // If the type assertion succeeds, we set the appropriate language data | |
| 32 | + | if data, ok := out.(*StringsYamlData); ok { | |
| 33 | + | switch lang { | |
| 34 | + | case "en": | |
| 35 | + | data.CurrentLang = data.EN | |
| 36 | + | case "fr": | |
| 37 | + | data.CurrentLang = data.FR | |
| 38 | + | default: // default to English if no match | |
| 39 | + | data.CurrentLang = data.EN | |
| 40 | + | } | |
| 41 | + | } else { | |
| 42 | + | return fmt.Errorf("failed to assert type of out interface: %w", err) | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | return nil | |
| 46 | + | } | |
| @@ -0,0 +1,46 @@ | |||||
| 1 | + | package webui | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "fmt" | |||
| 5 | + | "io/fs" | |||
| 6 | + | ||||
| 7 | + | "github.com/bnema/gordon/pkg/utils/parser" | |||
| 8 | + | ) | |||
| 9 | + | ||||
| 10 | + | type LangData map[string]interface{} | |||
| 11 | + | ||||
| 12 | + | type StringsModel struct { | |||
| 13 | + | Header string `yaml:"header"` | |||
| 14 | + | Subheader string `yaml:"subheader"` | |||
| 15 | + | } | |||
| 16 | + | type StringsYamlData struct { | |||
| 17 | + | StringsModel StringsModel `yaml:"strings_model"` | |||
| 18 | + | EN LangData `yaml:"en"` | |||
| 19 | + | FR LangData `yaml:"fr"` | |||
| 20 | + | CurrentLang LangData | |||
| 21 | + | } | |||
| 22 | + | ||||
| 23 | + | // ReadStringsDataFromYAML reads the strings.yaml file and unmarshals it into the out interface | |||
| 24 | + | func ReadStringsDataFromYAML(lang string, fs fs.FS, filename string, out interface{}) error { | |||
| 25 | + | ||||
| 26 | + | err := parser.OpenYamlFile(fs, filename, out) | |||
| 27 | + | if err != nil { | |||
| 28 | + | return err | |||
| 29 | + | } | |||
| 30 | + | ||||
| 31 | + | // If the type assertion succeeds, we set the appropriate language data | |||
| 32 | + | if data, ok := out.(*StringsYamlData); ok { | |||
| 33 | + | switch lang { | |||
| 34 | + | case "en": | |||
| 35 | + | data.CurrentLang = data.EN | |||
| 36 | + | case "fr": | |||
| 37 | + | data.CurrentLang = data.FR | |||
| 38 | + | default: // default to English if no match | |||
| 39 | + | data.CurrentLang = data.EN | |||
| 40 | + | } | |||
| 41 | + | } else { | |||
| 42 | + | return fmt.Errorf("failed to assert type of out interface: %w", err) | |||
| 43 | + | } | |||
| 44 | + | ||||
| 45 | + | return nil | |||
| 46 | + | } | |||
R internal/gotemplate/models/html/strings.yml → internal/webui/strings.yml +0 -0 renamed, 100% similar
renamed, 100% similar
A pkg/utils/parser/yaml.go +32 -0
| @@ -0,0 +1,32 @@ | |||
| 1 | + | package parser | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "fmt" | |
| 5 | + | "io" | |
| 6 | + | "io/fs" | |
| 7 | + | ||
| 8 | + | "gopkg.in/yaml.v3" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | // OpenYamlFile opens a YAML file and unmarshals it into the out interface | |
| 12 | + | func OpenYamlFile(fs fs.FS, filename string, out interface{}) error { | |
| 13 | + | // Open the YAML file | |
| 14 | + | file, err := fs.Open(filename) | |
| 15 | + | if err != nil { | |
| 16 | + | return fmt.Errorf("failed to open %s: %w", filename, err) | |
| 17 | + | } | |
| 18 | + | defer file.Close() | |
| 19 | + | ||
| 20 | + | // Read the content of the file | |
| 21 | + | content, err := io.ReadAll(file) | |
| 22 | + | if err != nil { | |
| 23 | + | return fmt.Errorf("failed to read %s: %w", filename, err) | |
| 24 | + | } | |
| 25 | + | ||
| 26 | + | // Unmarshal the YAML content into the out interface | |
| 27 | + | if err := yaml.Unmarshal(content, out); err != nil { | |
| 28 | + | return fmt.Errorf("failed to unmarshal %s: %w", filename, err) | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | return nil | |
| 32 | + | } | |
| @@ -0,0 +1,32 @@ | |||||
| 1 | + | package parser | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "fmt" | |||
| 5 | + | "io" | |||
| 6 | + | "io/fs" | |||
| 7 | + | ||||
| 8 | + | "gopkg.in/yaml.v3" | |||
| 9 | + | ) | |||
| 10 | + | ||||
| 11 | + | // OpenYamlFile opens a YAML file and unmarshals it into the out interface | |||
| 12 | + | func OpenYamlFile(fs fs.FS, filename string, out interface{}) error { | |||
| 13 | + | // Open the YAML file | |||
| 14 | + | file, err := fs.Open(filename) | |||
| 15 | + | if err != nil { | |||
| 16 | + | return fmt.Errorf("failed to open %s: %w", filename, err) | |||
| 17 | + | } | |||
| 18 | + | defer file.Close() | |||
| 19 | + | ||||
| 20 | + | // Read the content of the file | |||
| 21 | + | content, err := io.ReadAll(file) | |||
| 22 | + | if err != nil { | |||
| 23 | + | return fmt.Errorf("failed to read %s: %w", filename, err) | |||
| 24 | + | } | |||
| 25 | + | ||||
| 26 | + | // Unmarshal the YAML content into the out interface | |||
| 27 | + | if err := yaml.Unmarshal(content, out); err != nil { | |||
| 28 | + | return fmt.Errorf("failed to unmarshal %s: %w", filename, err) | |||
| 29 | + | } | |||
| 30 | + | ||||
| 31 | + | return nil | |||
| 32 | + | } | |||