/
helpers.go
135 lines (113 loc) · 3.49 KB
/
helpers.go
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
125
126
127
128
129
130
131
132
133
134
135
package helpers
import (
"fmt"
"github.com/CloudyKit/jet/v6"
"github.com/justinas/nosurf"
"github.com/tsawler/vigilate/internal/config"
"github.com/tsawler/vigilate/internal/models"
"github.com/tsawler/vigilate/internal/templates"
"log"
"math/rand"
"net/http"
"runtime/debug"
"time"
)
const (
letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
letterIdxBits = 6 // 6 bits to represent a letter index
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
)
var app *config.AppConfig
var src = rand.NewSource(time.Now().UnixNano())
// NewHelpers creates new helpers
func NewHelpers(a *config.AppConfig) {
app = a
}
// IsAuthenticated returns true if a user is authenticated
func IsAuthenticated(r *http.Request) bool {
exists := app.Session.Exists(r.Context(), "userID")
return exists
}
// RandomString returns a random string of letters of length n
func RandomString(n int) string {
b := make([]byte, n)
for i, theCache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
if remain == 0 {
theCache, remain = src.Int63(), letterIdxMax
}
if idx := int(theCache & letterIdxMask); idx < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
theCache >>= letterIdxBits
remain--
}
return string(b)
}
// ServerError will display error page for internal server error
func ServerError(w http.ResponseWriter, r *http.Request, err error) {
trace := fmt.Sprintf("%s\n%s", err.Error(), debug.Stack())
_ = log.Output(2, trace)
w.WriteHeader(http.StatusInternalServerError)
w.Header().Set("Connection", "close")
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Header().Set("Cache-Control", "no-store, no-cache, must-revalidate, post-check=0, pre-check=0")
http.ServeFile(w, r, "./ui/static/500.html")
}
// views is the jet template set
var views = jet.NewSet(
jet.NewOSFileSystemLoader("./views"),
jet.InDevelopmentMode(),
)
// SetViews sets the view variable (for testing)
func SetViews(path string) {
views = jet.NewSet(
jet.NewOSFileSystemLoader(path),
jet.InDevelopmentMode(),
)
}
// DefaultData adds default data which is accessible to all templates
func DefaultData(td templates.TemplateData, r *http.Request, w http.ResponseWriter) templates.TemplateData {
td.CSRFToken = nosurf.Token(r)
td.IsAuthenticated = IsAuthenticated(r)
td.PreferenceMap = app.PreferenceMap
// if logged in, store user id in template data
if td.IsAuthenticated {
u := app.Session.Get(r.Context(), "user").(models.User)
td.User = u
}
td.Flash = app.Session.PopString(r.Context(), "flash")
td.Warning = app.Session.PopString(r.Context(), "warning")
td.Error = app.Session.PopString(r.Context(), "error")
return td
}
// RenderPage renders a page using jet templates
func RenderPage(w http.ResponseWriter, r *http.Request, templateName string, variables, data interface{}) error {
var vars jet.VarMap
if variables == nil {
vars = make(jet.VarMap)
} else {
vars = variables.(jet.VarMap)
}
// add default template data
var td templates.TemplateData
if data != nil {
td = data.(templates.TemplateData)
}
// add default data
td = DefaultData(td, r, w)
// add template functions
addTemplateFunctions()
// load the template and render it
t, err := views.GetTemplate(fmt.Sprintf("%s.jet", templateName))
if err != nil {
log.Println(err)
return err
}
if err = t.Execute(w, vars, td); err != nil {
log.Println(err)
return err
}
return nil
}