updated templater library
This commit is contained in:
@@ -1,14 +1,14 @@
|
||||
package templates
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"ruben/inventory2/internal/domains/accounts"
|
||||
etsy_platform "ruben/inventory2/internal/domains/platforms/etsy"
|
||||
@@ -43,23 +43,60 @@ func SetupRoutes(
|
||||
logger *logging.Logger,
|
||||
r gin.IRouter,
|
||||
contentDir string,
|
||||
tmpl *templater.Templater,
|
||||
rawEvents *raw_events.Store,
|
||||
accts *accounts.Store,
|
||||
etsy *etsy_platform.Platform,
|
||||
authMiddleware *middleware.Auth,
|
||||
) {
|
||||
|
||||
s := &webpageRouter{
|
||||
log: logger,
|
||||
contentDir: contentDir,
|
||||
templater: tmpl,
|
||||
log: logger,
|
||||
contentDir: contentDir,
|
||||
// TODO: clean up the references to the templates dir throughout as well (should only be referred to in the 'main' package
|
||||
templater: new(templater.Templater).With(templater.Config{
|
||||
Funcs: func(name string, props map[string]any) template.FuncMap {
|
||||
return template.FuncMap{
|
||||
// parsing
|
||||
"parseInt": func(s string) (int, error) {
|
||||
return strconv.Atoi(s)
|
||||
},
|
||||
"parseInt64": func(s string) (int64, error) {
|
||||
return strconv.ParseInt(s, 10, 64)
|
||||
},
|
||||
"parsePlatform": func(s string) (accounts.Platform, error) {
|
||||
return accounts.NewPlatform(s)
|
||||
},
|
||||
|
||||
// arithmetic
|
||||
"addInt": func(a, b int) int {
|
||||
return a + b
|
||||
},
|
||||
"subInt": func(a, b int) int {
|
||||
return a - b
|
||||
},
|
||||
"multInt": func(a, b int) int {
|
||||
return a * b
|
||||
},
|
||||
|
||||
// json
|
||||
"prettyPrintJSON": func(j json.RawMessage) string {
|
||||
b, err := json.MarshalIndent(j, " ", "")
|
||||
if err != nil {
|
||||
return string(j)
|
||||
}
|
||||
return string(b)
|
||||
},
|
||||
"marshalJSON": json.Marshal,
|
||||
}
|
||||
},
|
||||
}),
|
||||
rawEvents: rawEvents,
|
||||
accts: accts,
|
||||
etsy: etsy,
|
||||
authMiddleware: authMiddleware,
|
||||
}
|
||||
|
||||
fn2 := s.authMiddleware.AuthenticateAndAddIdentity(s.serveTemplates)
|
||||
authAndServeTemplate := s.authMiddleware.AuthenticateAndAddIdentity(s.serveTemplate)
|
||||
|
||||
r.GET("/*rest", response.Handler(func(c *gin.Context) (response.Response, error) {
|
||||
c.Request.URL.Path = c.Request.URL.Path[3:]
|
||||
@@ -73,48 +110,21 @@ func SetupRoutes(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.serveTemplates(c)
|
||||
return s.serveTemplate(c)
|
||||
}
|
||||
return fn2(c)
|
||||
|
||||
return authAndServeTemplate(c)
|
||||
}))
|
||||
}
|
||||
|
||||
// GET /
|
||||
// compiles the page template or component template matching the url
|
||||
// func (s *Server) serveTemplates(r *http.Request) (response.Response, error) {
|
||||
func (s *webpageRouter) serveTemplates(c *gin.Context) (response.Response, error) {
|
||||
func (s *webpageRouter) serveTemplate(c *gin.Context) (response.Response, error) {
|
||||
r := c.Request
|
||||
name, args := s.getTemplateNameAndArgs(r, s.contentDir+"/templates/components")
|
||||
fmt.Println("name:", name)
|
||||
fmt.Println("args:", args)
|
||||
|
||||
// TODO: update
|
||||
b, err := s.templater.ExecuteComponent(name, args...)
|
||||
if err == nil {
|
||||
return response.HTML(b), nil
|
||||
}
|
||||
|
||||
if !isFileNotFoundError(err) {
|
||||
return s.handleTemplateError(err, args...)
|
||||
}
|
||||
|
||||
name, args = s.getTemplateNameAndArgs(r, s.contentDir+"/templates/pages")
|
||||
fmt.Println("name (page):", name)
|
||||
fmt.Println("args (page):", args)
|
||||
|
||||
if b, err = s.templater.ExecutePage(name, args...); err == nil {
|
||||
return response.HTML(b), nil
|
||||
}
|
||||
|
||||
return s.handleTemplateError(err, args...)
|
||||
}
|
||||
|
||||
// func (s *Server) getTemplateNameAndArgs(r *http.Request, templateDir string) (name string, args []any) {
|
||||
func (s *webpageRouter) getTemplateNameAndArgs(r *http.Request, templateDir string) (name string, args []any) {
|
||||
ctx := r.Context()
|
||||
name, pathParams := getTemplateNameForURL(r.URL, templateDir)
|
||||
|
||||
return name, []any{
|
||||
b, err := s.templater.Execute(
|
||||
strings.Trim(r.URL.Path, "/"),
|
||||
"Request",
|
||||
r,
|
||||
// add services and data here
|
||||
@@ -122,95 +132,25 @@ func (s *webpageRouter) getTemplateNameAndArgs(r *http.Request, templateDir stri
|
||||
s.rawEvents.WithContext(ctx),
|
||||
"URLCalc",
|
||||
newURLCalculator(r.URL),
|
||||
"PathParams",
|
||||
pathParams,
|
||||
"Accounts",
|
||||
s.accts.WithContext(ctx),
|
||||
"Etsy",
|
||||
s.etsy.WithContext(ctx),
|
||||
|
||||
// TODO: apply auth to all templates needed!
|
||||
// auth tooling
|
||||
/*
|
||||
AccessToken string
|
||||
Claims authentication.AccessTokenClaims
|
||||
User accounts.OAuthUser
|
||||
Account *accounts.Account
|
||||
*/
|
||||
"Identity",
|
||||
middleware.GetIdentity(r.Context()),
|
||||
middleware.GetIdentity(ctx),
|
||||
"Auth",
|
||||
newTemplateAuthenticator(r),
|
||||
)
|
||||
if err != nil {
|
||||
return s.handleTemplateError(err)
|
||||
}
|
||||
|
||||
return response.HTML(b), nil
|
||||
}
|
||||
|
||||
// TODO: clean this up...
|
||||
// TODO: somehow tell what the path params are and pass them up.
|
||||
// - then consider pushing this functionality into the template library.
|
||||
//
|
||||
// getComponentTemplateNameForURL eliminate any trailing .html or /, and checks for any
|
||||
// file with path parameters in the name, eg '{abc}.html.tmpl', prefering exact filename matches.
|
||||
func getTemplateNameForURL(u *url.URL, templateDir string) (name string, params map[string]string) {
|
||||
templateDir = path.Clean(templateDir)
|
||||
fp := strings.TrimPrefix(strings.TrimSuffix(strings.TrimSuffix(u.Path, ".html"), "/"), "/")
|
||||
if fp == "" {
|
||||
// "/" maps to "/index"
|
||||
fp = "index"
|
||||
}
|
||||
|
||||
fpParts := strings.Split(fp, "/")
|
||||
res := getMatchingGlobPatternsCapturingFilepathIncludingParametrizedFilepaths(fpParts)
|
||||
for _, combs := range res {
|
||||
pattern := path.Join(templateDir, path.Join(combs...)) + ".html.tmpl"
|
||||
|
||||
matches, _ := filepath.Glob(pattern)
|
||||
if len(matches) == 0 {
|
||||
pattern := path.Join(templateDir, path.Join(combs...), "index") + ".html.tmpl"
|
||||
matches, _ = filepath.Glob(pattern)
|
||||
}
|
||||
if len(matches) > 0 {
|
||||
match := matches[0]
|
||||
name = strings.TrimPrefix(strings.TrimSuffix(match, ".html.tmpl"), templateDir+"/")
|
||||
|
||||
patternParts := strings.Split(name, "/")
|
||||
params = make(map[string]string)
|
||||
for i, pp := range patternParts {
|
||||
if strings.HasPrefix(pp, "{") && strings.HasSuffix(pp, "}") {
|
||||
params[pp[1:len(pp)-1]] = fpParts[i]
|
||||
}
|
||||
}
|
||||
|
||||
return name, params
|
||||
}
|
||||
}
|
||||
|
||||
return fp, nil
|
||||
}
|
||||
|
||||
func getMatchingGlobPatternsCapturingFilepathIncludingParametrizedFilepaths(filepathParts []string) [][]string {
|
||||
switch len(filepathParts) {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return [][]string{
|
||||
[]string{filepathParts[0]},
|
||||
[]string{"{*}"},
|
||||
}
|
||||
default:
|
||||
tailCombs := getMatchingGlobPatternsCapturingFilepathIncludingParametrizedFilepaths(filepathParts[1:])
|
||||
|
||||
combs := make([][]string, 2*len(tailCombs))
|
||||
for i, c := range tailCombs {
|
||||
combs[i*2] = append([]string{filepathParts[0]}, c...)
|
||||
combs[i*2+1] = append([]string{"{*}"}, c...)
|
||||
}
|
||||
|
||||
return combs
|
||||
}
|
||||
}
|
||||
|
||||
// func (s *Server) handleTemplateError(err error, templateArgs ...any) (response.Response, error) {
|
||||
func (s *webpageRouter) handleTemplateError(err error, templateArgs ...any) (response.Response, error) {
|
||||
func (s *webpageRouter) handleTemplateError(err error) (response.Response, error) {
|
||||
if isFileNotFoundError(err) {
|
||||
return nil, response.NotFound().
|
||||
Wrap(ErrTemplateNotFound{
|
||||
@@ -223,31 +163,10 @@ func (s *webpageRouter) handleTemplateError(err error, templateArgs ...any) (res
|
||||
}
|
||||
|
||||
func isFileNotFoundError(err error) bool {
|
||||
/*
|
||||
var pe *os.PathError
|
||||
isPathErr := errors.As(err, &pe)
|
||||
|
||||
return isPathErr && pe.Err != nil && pe.Err.Error() == "no such file or directory"
|
||||
*/
|
||||
|
||||
var te *templater.ErrNotTemplateFileFound
|
||||
return errors.As(err, &te)
|
||||
}
|
||||
|
||||
func getHTTPStatusCode(err error) int {
|
||||
rerr, ok := response.GetError(err)
|
||||
if !ok {
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
|
||||
code, ok := rerr.GetStatus()
|
||||
if !ok {
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
|
||||
return code
|
||||
}
|
||||
|
||||
// template tooling
|
||||
|
||||
type URLCalculator struct {
|
||||
|
||||
Reference in New Issue
Block a user