aboutsummaryrefslogtreecommitdiff
path: root/echo/controller/generic_template.go
blob: 190182bb7db4d38f4f835fc84a7f84d782fde976 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package controller

import (
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"

	glecho "code.crute.us/mcrute/golib/echo"

	"github.com/labstack/echo/v4"
)

type FeatureFlags struct {
	flags map[string]bool
}

func NewFeatureFlags() *FeatureFlags {
	return &FeatureFlags{map[string]bool{}}
}

func (f *FeatureFlags) Need(feature string) string {
	f.flags[feature] = true
	return ""
}

func (f *FeatureFlags) Needs(feature string) bool {
	v, ok := f.flags[feature]
	return ok && v
}

type PageContext struct {
	c map[string]string
}

func NewPageContext() *PageContext {
	return &PageContext{map[string]string{}}
}

func (c *PageContext) HasKey(key string) bool {
	_, ok := c.c[key]
	return ok
}

func (c *PageContext) Set(key, value string) string {
	c.c[key] = value
	return ""
}

func (c *PageContext) Get(key string) string {
	return c.c[key]
}

type GenericTemplateHandler struct {
	Render func(echo.Context) (interface{}, error)
	cb     string
	init   sync.Once
	cbm    sync.Mutex // Should only be used in Debug
}

func (h *GenericTemplateHandler) TmplMakeCacheBustUrl(su string) (string, error) {
	u, err := url.Parse(su)
	if err != nil {
		return "", err
	}

	q := u.Query()
	q.Add("cb", h.cb)
	u.RawQuery = q.Encode()

	return u.String(), nil
}

func (h *GenericTemplateHandler) canonicalizeUrl(c echo.Context, tr glecho.TemplateChecker) string {
	path := strings.Trim(c.Request().URL.Path, "/")
	if path == "" {
		path = "index.tpl"
	} else if !strings.HasSuffix(path, ".html") {
		p := fmt.Sprintf("%s.tpl", path)
		if !tr.HaveTemplate(c, p) {
			path = fmt.Sprintf("%s/index.tpl", path)
		} else {
			path = p
		}
	} else {
		path = strings.ReplaceAll(path, ".html", ".tpl")
	}
	return path
}

func (h *GenericTemplateHandler) Handle(c echo.Context) error {
	// Generate the cache-buster only once at first request
	h.init.Do(func() {
		h.cb = strconv.FormatInt(time.Now().Unix(), 16)
	})

	// If in debug mode use a new cache buster every time to make reloading
	// assets easier
	if c.Echo().Debug {
		h.cbm.Lock()
		h.cb = strconv.FormatInt(time.Now().Unix(), 16)
		h.cbm.Unlock()
	}

	tr, ok := c.Echo().Renderer.(glecho.TemplateChecker)
	if !ok {
		// The only reason this should happen is if the app is wired wrong
		panic("Template renderer must implement TemplateChecker")
	}

	path := h.canonicalizeUrl(c, tr)

	c.Set("CanonicalPath", path)

	data, err := h.Render(c)
	if err != nil {
		return err
	}

	return c.Render(http.StatusOK, path, data)
}