diff options
Diffstat (limited to 'echo/controller/generic_template.go')
-rw-r--r-- | echo/controller/generic_template.go | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/echo/controller/generic_template.go b/echo/controller/generic_template.go new file mode 100644 index 0000000..190182b --- /dev/null +++ b/echo/controller/generic_template.go | |||
@@ -0,0 +1,124 @@ | |||
1 | package controller | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "net/http" | ||
6 | "net/url" | ||
7 | "strconv" | ||
8 | "strings" | ||
9 | "sync" | ||
10 | "time" | ||
11 | |||
12 | glecho "code.crute.us/mcrute/golib/echo" | ||
13 | |||
14 | "github.com/labstack/echo/v4" | ||
15 | ) | ||
16 | |||
17 | type FeatureFlags struct { | ||
18 | flags map[string]bool | ||
19 | } | ||
20 | |||
21 | func NewFeatureFlags() *FeatureFlags { | ||
22 | return &FeatureFlags{map[string]bool{}} | ||
23 | } | ||
24 | |||
25 | func (f *FeatureFlags) Need(feature string) string { | ||
26 | f.flags[feature] = true | ||
27 | return "" | ||
28 | } | ||
29 | |||
30 | func (f *FeatureFlags) Needs(feature string) bool { | ||
31 | v, ok := f.flags[feature] | ||
32 | return ok && v | ||
33 | } | ||
34 | |||
35 | type PageContext struct { | ||
36 | c map[string]string | ||
37 | } | ||
38 | |||
39 | func NewPageContext() *PageContext { | ||
40 | return &PageContext{map[string]string{}} | ||
41 | } | ||
42 | |||
43 | func (c *PageContext) HasKey(key string) bool { | ||
44 | _, ok := c.c[key] | ||
45 | return ok | ||
46 | } | ||
47 | |||
48 | func (c *PageContext) Set(key, value string) string { | ||
49 | c.c[key] = value | ||
50 | return "" | ||
51 | } | ||
52 | |||
53 | func (c *PageContext) Get(key string) string { | ||
54 | return c.c[key] | ||
55 | } | ||
56 | |||
57 | type GenericTemplateHandler struct { | ||
58 | Render func(echo.Context) (interface{}, error) | ||
59 | cb string | ||
60 | init sync.Once | ||
61 | cbm sync.Mutex // Should only be used in Debug | ||
62 | } | ||
63 | |||
64 | func (h *GenericTemplateHandler) TmplMakeCacheBustUrl(su string) (string, error) { | ||
65 | u, err := url.Parse(su) | ||
66 | if err != nil { | ||
67 | return "", err | ||
68 | } | ||
69 | |||
70 | q := u.Query() | ||
71 | q.Add("cb", h.cb) | ||
72 | u.RawQuery = q.Encode() | ||
73 | |||
74 | return u.String(), nil | ||
75 | } | ||
76 | |||
77 | func (h *GenericTemplateHandler) canonicalizeUrl(c echo.Context, tr glecho.TemplateChecker) string { | ||
78 | path := strings.Trim(c.Request().URL.Path, "/") | ||
79 | if path == "" { | ||
80 | path = "index.tpl" | ||
81 | } else if !strings.HasSuffix(path, ".html") { | ||
82 | p := fmt.Sprintf("%s.tpl", path) | ||
83 | if !tr.HaveTemplate(c, p) { | ||
84 | path = fmt.Sprintf("%s/index.tpl", path) | ||
85 | } else { | ||
86 | path = p | ||
87 | } | ||
88 | } else { | ||
89 | path = strings.ReplaceAll(path, ".html", ".tpl") | ||
90 | } | ||
91 | return path | ||
92 | } | ||
93 | |||
94 | func (h *GenericTemplateHandler) Handle(c echo.Context) error { | ||
95 | // Generate the cache-buster only once at first request | ||
96 | h.init.Do(func() { | ||
97 | h.cb = strconv.FormatInt(time.Now().Unix(), 16) | ||
98 | }) | ||
99 | |||
100 | // If in debug mode use a new cache buster every time to make reloading | ||
101 | // assets easier | ||
102 | if c.Echo().Debug { | ||
103 | h.cbm.Lock() | ||
104 | h.cb = strconv.FormatInt(time.Now().Unix(), 16) | ||
105 | h.cbm.Unlock() | ||
106 | } | ||
107 | |||
108 | tr, ok := c.Echo().Renderer.(glecho.TemplateChecker) | ||
109 | if !ok { | ||
110 | // The only reason this should happen is if the app is wired wrong | ||
111 | panic("Template renderer must implement TemplateChecker") | ||
112 | } | ||
113 | |||
114 | path := h.canonicalizeUrl(c, tr) | ||
115 | |||
116 | c.Set("CanonicalPath", path) | ||
117 | |||
118 | data, err := h.Render(c) | ||
119 | if err != nil { | ||
120 | return err | ||
121 | } | ||
122 | |||
123 | return c.Render(http.StatusOK, path, data) | ||
124 | } | ||