cmd/golink: use html/template when rendering pages

Properly use html/template rather than text/template so that we don't
have to worry about escaping data that is passed in to the templates.

Also move the success page into a template for both better rendering and
consistency.

Finally, this includes some tailwind changes that apparently got missed
in previous commits.

Change-Id: Ic658ab940888c44483501e33699da9021be18ecf
This commit is contained in:
Will Norris
2022-06-28 15:35:25 -07:00
parent 2a38b842f7
commit c7f68940d6
3 changed files with 26 additions and 34 deletions
+12 -8
View File
@@ -10,7 +10,7 @@ import (
"errors"
"flag"
"fmt"
"html"
"html/template"
"io/fs"
"io/ioutil"
"log"
@@ -21,7 +21,7 @@ import (
"strconv"
"strings"
"sync"
"text/template"
texttemplate "text/template"
"time"
"tailscale.com/client/tailscale"
@@ -184,6 +184,9 @@ var homeTmpl *template.Template
// helpTmpl is the template used by the http://go/.help page
var helpTmpl *template.Template
// successTmpl is the template used when a link is successfully created or updated.
var successTmpl *template.Template
type visitData struct {
Short string
NumClicks int
@@ -198,6 +201,7 @@ type homeData struct {
func init() {
homeTmpl = template.Must(template.ParseFS(embeddedFS, "tmpl/base.html", "tmpl/home.html"))
helpTmpl = template.Must(template.ParseFS(embeddedFS, "tmpl/base.html", "tmpl/help.html"))
successTmpl = template.Must(template.ParseFS(embeddedFS, "tmpl/base.html", "tmpl/success.html"))
}
// initStats initializes the in-memory stats counter with counts from db.
@@ -244,7 +248,7 @@ func serveHome(w http.ResponseWriter, short string) {
stats.mu.Lock()
for short, numClicks := range stats.clicks {
clicks = append(clicks, visitData{
Short: html.EscapeString(short),
Short: short,
NumClicks: numClicks,
})
}
@@ -261,7 +265,7 @@ func serveHome(w http.ResponseWriter, short string) {
})
homeTmpl.Execute(w, homeData{
Short: html.EscapeString(short),
Short: short,
Clicks: clicks,
})
}
@@ -339,7 +343,7 @@ type expandEnv struct {
Path string
}
var expandFuncMap = template.FuncMap{
var expandFuncMap = texttemplate.FuncMap{
"PathEscape": url.PathEscape,
"QueryEscape": url.QueryEscape,
}
@@ -358,7 +362,7 @@ func expandLink(long string, env expandEnv) (string, error) {
long += "{{with .Path}}/{{.}}{{end}}"
}
}
tmpl, err := template.New("").Funcs(expandFuncMap).Parse(long)
tmpl, err := texttemplate.New("").Funcs(expandFuncMap).Parse(long)
if err != nil {
return "", err
}
@@ -419,7 +423,7 @@ func serveSave(w http.ResponseWriter, r *http.Request) {
http.Error(w, "short may only contain letters, numbers, dash, and period", http.StatusBadRequest)
return
}
if _, err := template.New("").Funcs(expandFuncMap).Parse(long); err != nil {
if _, err := texttemplate.New("").Funcs(expandFuncMap).Parse(long); err != nil {
http.Error(w, fmt.Sprintf("long contains an invalid template: %v", err), http.StatusBadRequest)
return
}
@@ -461,7 +465,7 @@ func serveSave(w http.ResponseWriter, r *http.Request) {
}
if strings.Contains(strings.ToLower(r.Header.Get("Accept")), "text/html") {
fmt.Fprintf(w, "<h1>saved</h1>made <a href='http://go/%s'>http://go/%s</a>", html.EscapeString(short), html.EscapeString(short))
successTmpl.Execute(w, homeData{Short: short})
} else {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(link)
+9 -26
View File
@@ -672,7 +672,7 @@ select {
}
.prose {
color: #333;
color: var(--tw-prose-body);
max-width: 65ch;
}
@@ -685,15 +685,11 @@ select {
}
.prose :where(a):not(:where([class~="not-prose"] *)) {
color: #3182ce;
color: var(--tw-prose-links);
text-decoration: underline;
font-weight: 500;
}
.prose :where(a):not(:where([class~="not-prose"] *)):hover {
color: #2c5282;
}
.prose :where(strong):not(:where([class~="not-prose"] *)) {
color: var(--tw-prose-bold);
font-weight: 600;
@@ -1138,18 +1134,14 @@ select {
min-height: 100vh;
}
.max-w-4xl {
max-width: 56rem;
.max-w-5xl {
max-width: 64rem;
}
.max-w-full {
max-width: 100%;
}
.max-w-5xl {
max-width: 64rem;
}
.flex-1 {
flex: 1 1 0%;
}
@@ -1290,16 +1282,16 @@ select {
line-height: 1.75rem;
}
.text-xs {
font-size: 0.75rem;
line-height: 1rem;
}
.text-sm {
font-size: 0.875rem;
line-height: 1.25rem;
}
.text-xs {
font-size: 0.75rem;
line-height: 1rem;
}
.font-bold {
font-weight: 700;
}
@@ -1327,15 +1319,6 @@ select {
color: rgb(159 153 149 / var(--tw-text-opacity));
}
.text-blue-200 {
color: rgba(43, 123, 185, 0.15);
}
.text-blue-500 {
--tw-text-opacity: 1;
color: rgb(74 125 221 / var(--tw-text-opacity));
}
.text-blue-600 {
--tw-text-opacity: 1;
color: rgb(77 120 200 / var(--tw-text-opacity));
+5
View File
@@ -0,0 +1,5 @@
{{ define "main" }}
<h2 class="text-xl font-bold pb-2">Success</h2>
<p><a class="text-blue-600 hover:underline" href="http://go/{{.Short}}">go/{{.Short}}</a> has been saved.</p>
{{ end }}