From 7e11af36f364eefdbd0745a01022a74407609271 Mon Sep 17 00:00:00 2001 From: Armando Cerna Date: Mon, 8 Jul 2024 16:40:06 -0400 Subject: [PATCH 1/2] Pass hostname to all html templating --- golink.go | 49 +++++++++++++++++++++++++++++++---------------- tmpl/all.html | 6 +++--- tmpl/base.html | 4 ++-- tmpl/delete.html | 4 ++-- tmpl/detail.html | 4 ++-- tmpl/help.html | 6 +++--- tmpl/home.html | 6 +++--- tmpl/success.html | 2 +- 8 files changed, 48 insertions(+), 33 deletions(-) diff --git a/golink.go b/golink.go index da09850..1d3524e 100644 --- a/golink.go +++ b/golink.go @@ -269,17 +269,28 @@ type visitData struct { // homeData is the data used by homeTmpl. type homeData struct { - Short string - Long string - Clicks []visitData - XSRF string + Hostname string + Short string + Long string + Clicks []visitData + XSRF string +} + +type helpData struct { + Hostname string +} + +type allData struct { + Hostname string + Links []*Link } // deleteData is the data used by deleteTmpl. type deleteData struct { - Short string - Long string - XSRF string + Hostname string + Short string + Long string + XSRF string } var xsrfKey string @@ -443,10 +454,11 @@ func serveHome(w http.ResponseWriter, r *http.Request, short string) { return } homeTmpl.Execute(w, homeData{ - Short: short, - Long: long, - Clicks: clicks, - XSRF: xsrftoken.Generate(xsrfKey, cu.login, newShortName), + Hostname: *hostname, + Short: short, + Long: long, + Clicks: clicks, + XSRF: xsrftoken.Generate(xsrfKey, cu.login, newShortName), }) } @@ -465,11 +477,11 @@ func serveAll(w http.ResponseWriter, _ *http.Request) { return links[i].Short < links[j].Short }) - allTmpl.Execute(w, links) + allTmpl.Execute(w, allData{Links: links, Hostname: *hostname}) } func serveHelp(w http.ResponseWriter, _ *http.Request) { - helpTmpl.Execute(w, nil) + helpTmpl.Execute(w, helpData{Hostname: *hostname}) } func serveOpenSearch(w http.ResponseWriter, _ *http.Request) { @@ -549,6 +561,7 @@ func acceptHTML(r *http.Request) bool { // detailData is the data used by the detailTmpl template. type detailData struct { + Hostname string // Editable indicates whether the current user can edit the link. Editable bool Link *Link @@ -594,6 +607,7 @@ func serveDetail(w http.ResponseWriter, r *http.Request) { } data := detailData{ + Hostname: *hostname, Link: link, Editable: canEdit, XSRF: xsrftoken.Generate(xsrfKey, cu.login, link.Short), @@ -794,9 +808,10 @@ func serveDelete(w http.ResponseWriter, r *http.Request) { deleteLinkStats(link) deleteTmpl.Execute(w, deleteData{ - Short: link.Short, - Long: link.Long, - XSRF: xsrftoken.Generate(xsrfKey, cu.login, newShortName), + Hostname: *hostname, + Short: link.Short, + Long: link.Long, + XSRF: xsrftoken.Generate(xsrfKey, cu.login, newShortName), }) } @@ -879,7 +894,7 @@ func serveSave(w http.ResponseWriter, r *http.Request) { } if acceptHTML(r) { - successTmpl.Execute(w, homeData{Short: short}) + successTmpl.Execute(w, homeData{Short: short, Hostname: *hostname}) } else { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(link) diff --git a/tmpl/all.html b/tmpl/all.html index fab338e..852661b 100644 --- a/tmpl/all.html +++ b/tmpl/all.html @@ -1,5 +1,5 @@ {{ define "main" }} -

All Links ({{ len . }} total)

+

All Links ({{ len .Links }} total)

@@ -9,11 +9,11 @@

All Links ({{ len . }} total)

- {{ range . }} + {{ range .Links }}
- go/{{ .Short }} + {{ $.Hostname }}/{{ .Short }} diff --git a/tmpl/base.html b/tmpl/base.html index c723384..a5926e4 100644 --- a/tmpl/base.html +++ b/tmpl/base.html @@ -1,7 +1,7 @@ - go/ + {{ .Hostname }}/ @@ -11,7 +11,7 @@
-

go/

+

{{ .Hostname }}/

A private shortlink service for your tailnet
diff --git a/tmpl/delete.html b/tmpl/delete.html index a4d5bf5..b864c4a 100644 --- a/tmpl/delete.html +++ b/tmpl/delete.html @@ -1,5 +1,5 @@ {{ define "main" }} -

Link go/{{.Short}} Deleted

+

Link {{ .Hostname }}/{{.Short}} Deleted

Deleted this by mistake? You can recreate the same link below.

@@ -7,7 +7,7 @@

Link go/{{.Short}} Deleted

- + diff --git a/tmpl/detail.html b/tmpl/detail.html index 0c5dbf6..2c1e84e 100644 --- a/tmpl/detail.html +++ b/tmpl/detail.html @@ -6,7 +6,7 @@

Link Details

- + @@ -40,7 +40,7 @@

Danger Zone

{{ else }}
Name
-
go/{{.Link.Short}}
+
{{.Hostname}}/{{.Link.Short}}
Destination
{{.Link.Long}}
diff --git a/tmpl/help.html b/tmpl/help.html index 81a41ab..2a2a4a4 100644 --- a/tmpl/help.html +++ b/tmpl/help.html @@ -113,10 +113,10 @@

Application Programming Interface (API)

-Visit go/.export to export all saved links and their metadata in JSON Lines format. +Visit {{.Hostname}}/.export to export all saved links and their metadata in JSON Lines format. This is useful to create data snapshots that can be restored later. -

{{`$ curl -L go/.export
+
{{`$ curl -L {{.Hostname}}/.export
 {"Short":"go","Long":"http://go","Created":"2022-05-31T13:04:44.741457796-07:00","LastEdit":"2022-05-31T13:04:44.741457796-07:00","Owner":"amelie@example.com","Clicks":1}
 {"Short":"slack","Long":"https://company.slack.com/{{if .Path}}channels/{{PathEscape .Path}}{{end}}","Created":"2022-06-17T18:05:43.562948451Z","LastEdit":"2022-06-17T18:06:35.811398Z","Owner":"amelie@example.com","Clicks":4}`}}
 
@@ -124,7 +124,7 @@

Application Programming Interface (API)

Create a new link by sending a POST request with a short and long value: -

{{`$ curl -L -H Sec-Golink:1 -d short=cs -d long=https://cs.github.com/ go
+
{{`$ curl -L -H Sec-Golink:1 -d short=cs -d long=https://cs.github.com/ {{.Hostname}}
 {"Short":"cs","Long":"https://cs.github.com/","Created":"2022-06-03T22:15:29.993978392Z","LastEdit":"2022-06-03T22:15:29.993978392Z","Owner":"amelie@example.com"}`}}
 
diff --git a/tmpl/home.html b/tmpl/home.html index 64e2267..bf1ea68 100644 --- a/tmpl/home.html +++ b/tmpl/home.html @@ -2,12 +2,12 @@

Create a new link

{{ with .Long }} -

Did you mean {{.}} ? Create a go link for it now:

+

Did you mean {{.}} ? Create a {{.Hostname}} link for it now:

{{ end }}
- + @@ -29,7 +29,7 @@

Popular Links

{{range .Clicks}}
- go/{{.Short}} + {{.Hostname}}/{{.Short}} diff --git a/tmpl/success.html b/tmpl/success.html index 24c0548..8659c6e 100644 --- a/tmpl/success.html +++ b/tmpl/success.html @@ -1,5 +1,5 @@ {{ define "main" }}

Success

-

go/{{.Short}} has been saved.

+

{{.Hostname}}/{{.Short}} has been saved.

{{ end }} From 5d45925b92997ce3c3953f36df49aaac2015b296 Mon Sep 17 00:00:00 2001 From: Will Norris Date: Mon, 8 Jul 2024 22:01:19 -0700 Subject: [PATCH 2/2] use a template func rather than var for custom hostname Signed-off-by: Will Norris --- golink.go | 91 +++++++++++++++++++++++---------------------- tmpl/all.html | 6 +-- tmpl/base.html | 4 +- tmpl/delete.html | 4 +- tmpl/detail.html | 4 +- tmpl/help.html | 48 ++++++++++++------------ tmpl/home.html | 6 +-- tmpl/opensearch.xml | 8 ++-- tmpl/success.html | 2 +- 9 files changed, 88 insertions(+), 85 deletions(-) diff --git a/golink.go b/golink.go index 1d3524e..01684e0 100644 --- a/golink.go +++ b/golink.go @@ -269,46 +269,57 @@ type visitData struct { // homeData is the data used by homeTmpl. type homeData struct { - Hostname string - Short string - Long string - Clicks []visitData - XSRF string -} - -type helpData struct { - Hostname string -} - -type allData struct { - Hostname string - Links []*Link + Short string + Long string + Clicks []visitData + XSRF string } // deleteData is the data used by deleteTmpl. type deleteData struct { - Hostname string - Short string - Long string - XSRF string + Short string + Long string + XSRF string } var xsrfKey string func init() { - homeTmpl = template.Must(template.ParseFS(embeddedFS, "tmpl/base.html", "tmpl/home.html")) - detailTmpl = template.Must(template.ParseFS(embeddedFS, "tmpl/base.html", "tmpl/detail.html")) - successTmpl = template.Must(template.ParseFS(embeddedFS, "tmpl/base.html", "tmpl/success.html")) - helpTmpl = template.Must(template.ParseFS(embeddedFS, "tmpl/base.html", "tmpl/help.html")) - allTmpl = template.Must(template.ParseFS(embeddedFS, "tmpl/base.html", "tmpl/all.html")) - deleteTmpl = template.Must(template.ParseFS(embeddedFS, "tmpl/base.html", "tmpl/delete.html")) - opensearchTmpl = template.Must(template.ParseFS(embeddedFS, "tmpl/opensearch.xml")) + homeTmpl = newTemplate("base.html", "home.html") + detailTmpl = newTemplate("base.html", "detail.html") + successTmpl = newTemplate("base.html", "success.html") + helpTmpl = newTemplate("base.html", "help.html") + allTmpl = newTemplate("base.html", "all.html") + deleteTmpl = newTemplate("base.html", "delete.html") + opensearchTmpl = newTemplate("opensearch.xml") b := make([]byte, 24) rand.Read(b) xsrfKey = base64.StdEncoding.EncodeToString(b) } +var tmplFuncs = template.FuncMap{ + "go": func() string { + return *hostname + }, +} + +// newTemplate creates a new template with the specified files in the tmpl directory. +// The first file name is used as the template name, +// and tmplFuncs are registered as available funcs. +// This func panics if unable to parse files. +func newTemplate(files ...string) *template.Template { + if len(files) == 0 { + return nil + } + tf := make([]string, 0, len(files)) + for _, f := range files { + tf = append(tf, "tmpl/"+f) + } + t := template.New(files[0]).Funcs(tmplFuncs) + return template.Must(t.ParseFS(embeddedFS, tf...)) +} + // initStats initializes the in-memory stats counter with counts from db. func initStats() error { stats.mu.Lock() @@ -454,11 +465,10 @@ func serveHome(w http.ResponseWriter, r *http.Request, short string) { return } homeTmpl.Execute(w, homeData{ - Hostname: *hostname, - Short: short, - Long: long, - Clicks: clicks, - XSRF: xsrftoken.Generate(xsrfKey, cu.login, newShortName), + Short: short, + Long: long, + Clicks: clicks, + XSRF: xsrftoken.Generate(xsrfKey, cu.login, newShortName), }) } @@ -477,20 +487,16 @@ func serveAll(w http.ResponseWriter, _ *http.Request) { return links[i].Short < links[j].Short }) - allTmpl.Execute(w, allData{Links: links, Hostname: *hostname}) + allTmpl.Execute(w, links) } func serveHelp(w http.ResponseWriter, _ *http.Request) { - helpTmpl.Execute(w, helpData{Hostname: *hostname}) + helpTmpl.Execute(w, nil) } func serveOpenSearch(w http.ResponseWriter, _ *http.Request) { - type opensearchData struct { - Hostname string - } - w.Header().Set("Content-Type", "application/opensearchdescription+xml") - opensearchTmpl.Execute(w, opensearchData{Hostname: *hostname}) + opensearchTmpl.Execute(w, nil) } func serveGo(w http.ResponseWriter, r *http.Request) { @@ -561,7 +567,6 @@ func acceptHTML(r *http.Request) bool { // detailData is the data used by the detailTmpl template. type detailData struct { - Hostname string // Editable indicates whether the current user can edit the link. Editable bool Link *Link @@ -607,7 +612,6 @@ func serveDetail(w http.ResponseWriter, r *http.Request) { } data := detailData{ - Hostname: *hostname, Link: link, Editable: canEdit, XSRF: xsrftoken.Generate(xsrfKey, cu.login, link.Short), @@ -808,10 +812,9 @@ func serveDelete(w http.ResponseWriter, r *http.Request) { deleteLinkStats(link) deleteTmpl.Execute(w, deleteData{ - Hostname: *hostname, - Short: link.Short, - Long: link.Long, - XSRF: xsrftoken.Generate(xsrfKey, cu.login, newShortName), + Short: link.Short, + Long: link.Long, + XSRF: xsrftoken.Generate(xsrfKey, cu.login, newShortName), }) } @@ -894,7 +897,7 @@ func serveSave(w http.ResponseWriter, r *http.Request) { } if acceptHTML(r) { - successTmpl.Execute(w, homeData{Short: short, Hostname: *hostname}) + successTmpl.Execute(w, homeData{Short: short}) } else { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(link) diff --git a/tmpl/all.html b/tmpl/all.html index 852661b..b28c84e 100644 --- a/tmpl/all.html +++ b/tmpl/all.html @@ -1,5 +1,5 @@ {{ define "main" }} -

All Links ({{ len .Links }} total)

+

All Links ({{ len . }} total)

@@ -9,11 +9,11 @@

All Links ({{ len .Links }} total)

- {{ range .Links }} + {{ range . }}
- {{ $.Hostname }}/{{ .Short }} + {{go}}/{{ .Short }} diff --git a/tmpl/base.html b/tmpl/base.html index a5926e4..792bba8 100644 --- a/tmpl/base.html +++ b/tmpl/base.html @@ -1,7 +1,7 @@ - {{ .Hostname }}/ + {{go}}/ @@ -11,7 +11,7 @@
-

{{ .Hostname }}/

+

{{go}}/

A private shortlink service for your tailnet
diff --git a/tmpl/delete.html b/tmpl/delete.html index b864c4a..b84edc4 100644 --- a/tmpl/delete.html +++ b/tmpl/delete.html @@ -1,5 +1,5 @@ {{ define "main" }} -

Link {{ .Hostname }}/{{.Short}} Deleted

+

Link {{go}}/{{.Short}} Deleted

Deleted this by mistake? You can recreate the same link below.

@@ -7,7 +7,7 @@

Link {{ .Hostname }}/{{.Short}} Deleted

- + diff --git a/tmpl/detail.html b/tmpl/detail.html index 2c1e84e..95adb19 100644 --- a/tmpl/detail.html +++ b/tmpl/detail.html @@ -6,7 +6,7 @@

Link Details

- + @@ -40,7 +40,7 @@

Danger Zone

{{ else }}
Name
-
{{.Hostname}}/{{.Link.Short}}
+
{{go}}/{{.Link.Short}}
Destination
{{.Link.Long}}
diff --git a/tmpl/help.html b/tmpl/help.html index 2a2a4a4..a7f1214 100644 --- a/tmpl/help.html +++ b/tmpl/help.html @@ -1,19 +1,19 @@ {{ define "main" }}

-go links provide short, memorable links for the websites you and your team use most. +{{go}} links provide short, memorable links for the websites you and your team use most. -

Creating go links

+

Creating {{go}} links

-All go links have a short name and a destination link that the go link points to. +All {{go}} links have a short name and a destination link that the {{go}} link points to. Some notes on short names:

  • names must start with a letter or number
  • names may contain letters, numbers, hyphens, and periods -
  • names are not case-sensitive (go/foo is the same as go/FOO) -
  • hyphens are ignored when resolving links (go/meetingnotes is the same as go/meeting-notes) +
  • names are not case-sensitive ({{go}}/foo is the same as {{go}}/FOO) +
  • hyphens are ignored when resolving links ({{go}}/meetingnotes is the same as {{go}}/meeting-notes)

@@ -25,13 +25,13 @@

Creating go links

Resolving links

-When logged in to your Tailscale network, go links can be entered directly into any browser or command line utility such as curl. +When logged in to your Tailscale network, {{go}} links can be entered directly into any browser or command line utility such as curl. You do not need any additional browser extensions.

Any additional path provided after the short name will be added to the end of the destination link. -For example, if go/who goes to your company directory at http://directory/, -then go/who/amelie will go to http://directory/amelie. +For example, if {{go}}/who goes to your company directory at http://directory/, +then {{go}}/who/amelie will go to http://directory/amelie.

Advanced destination links allow you to further customize this behavior. @@ -39,12 +39,12 @@

Resolving links

Advanced destination links

-To have more control over how go links are resolved, destination links can use Go template syntax. +To have more control over how {{go}} links are resolved, destination links can use Go template syntax. Templates are provided a data structure with the following fields:

  • .Path is the remaining path value after the short name (without a leading slash). - For the link go/who/amelie, the value of .Path is amelie. + For the link {{go}}/who/amelie, the value of .Path is amelie.
  • .Now is a time.Time value representing the current date and time.
  • .User is the current user resolving the link. This is the email address of the user or {username}@github for tailnets that use GitHub authentication. @@ -61,34 +61,34 @@

    Advanced destination links

    The most common use of advanced destination links is to put the additional path in a custom location in the destination link. -For example, you might set the destination for go/search to: +For example, you might set the destination for {{go}}/search to:

    {{`https://www.google.com/{{if .Path}}search?q={{QueryEscape .Path}}{{end}}`}}
    -When a user visits go/search with no additional path, they will be directed to https://www.google.com/. -If they include an additional path like go/search/pangolins, they will be directed to https://www.google.com/search?q=pangolins. +When a user visits {{go}}/search with no additional path, they will be directed to https://www.google.com/. +If they include an additional path like {{go}}/search/pangolins, they will be directed to https://www.google.com/search?q=pangolins.

    Examples

    - + - + - + - +
    Include path in querygo/search{{go}}/search {{`https://cloudsearch.google.com/{{if .Path}}cloudsearch/search?q={{QueryEscape .Path}}{{end}}`}}
    Include path in destination pathgo/slack{{go}}/slack {{`https://company.slack.com/{{if .Path}}channels/{{PathEscape .Path}}{{end}}`}}
    Include path in hostnamego/varz{{go}}/varz {{`http://{{if .Path}}{{.Path}}{{else}}host{{end}}.example/debug/varz`}}
    Include today's date in wiki pagego/today{{go}}/today {{`http://wiki/{{.Now.Format "01-02-2006"}}`}}
    @@ -101,8 +101,8 @@

    Application Programming Interface (API)

    Include a "+" after a link to get information about a link without resolving it: -

    {{`$ curl -L go/search+
    -{
    +
    $ curl -L {{go}}/search+
    +{{`{
     "Short": "search",
     "Long": "https://cloudsearch.google.com/{{if .Path}}cloudsearch/search?q={{QueryEscape .Path}}{{end}}",
     "Created": "2022-06-08T04:27:32.829906577Z",
    @@ -113,19 +113,19 @@ 

    Application Programming Interface (API)

    -Visit {{.Hostname}}/.export to export all saved links and their metadata in JSON Lines format. +Visit {{go}}/.export to export all saved links and their metadata in JSON Lines format. This is useful to create data snapshots that can be restored later. -

    {{`$ curl -L {{.Hostname}}/.export
    -{"Short":"go","Long":"http://go","Created":"2022-05-31T13:04:44.741457796-07:00","LastEdit":"2022-05-31T13:04:44.741457796-07:00","Owner":"amelie@example.com","Clicks":1}
    +
    $ curl -L {{go}}/.export
    +{{`{"Short":"go","Long":"http://go","Created":"2022-05-31T13:04:44.741457796-07:00","LastEdit":"2022-05-31T13:04:44.741457796-07:00","Owner":"amelie@example.com","Clicks":1}
     {"Short":"slack","Long":"https://company.slack.com/{{if .Path}}channels/{{PathEscape .Path}}{{end}}","Created":"2022-06-17T18:05:43.562948451Z","LastEdit":"2022-06-17T18:06:35.811398Z","Owner":"amelie@example.com","Clicks":4}`}}
     

    Create a new link by sending a POST request with a short and long value: -

    {{`$ curl -L -H Sec-Golink:1 -d short=cs -d long=https://cs.github.com/ {{.Hostname}}
    -{"Short":"cs","Long":"https://cs.github.com/","Created":"2022-06-03T22:15:29.993978392Z","LastEdit":"2022-06-03T22:15:29.993978392Z","Owner":"amelie@example.com"}`}}
    +
    $ curl -L -H Sec-Golink:1 -d short=cs -d long=https://cs.github.com/ {{go}}
    +{{`{"Short":"cs","Long":"https://cs.github.com/","Created":"2022-06-03T22:15:29.993978392Z","LastEdit":"2022-06-03T22:15:29.993978392Z","Owner":"amelie@example.com"}`}}
     
diff --git a/tmpl/home.html b/tmpl/home.html index bf1ea68..0e146be 100644 --- a/tmpl/home.html +++ b/tmpl/home.html @@ -2,12 +2,12 @@

Create a new link

{{ with .Long }} -

Did you mean {{.}} ? Create a {{.Hostname}} link for it now:

+

Did you mean {{.}} ? Create a {{go}} link for it now:

{{ end }}
- + @@ -29,7 +29,7 @@

Popular Links

{{range .Clicks}}
- {{.Hostname}}/{{.Short}} + {{go}}/{{.Short}} diff --git a/tmpl/opensearch.xml b/tmpl/opensearch.xml index a2bceaf..b30c1b1 100644 --- a/tmpl/opensearch.xml +++ b/tmpl/opensearch.xml @@ -1,8 +1,8 @@ - {{.Hostname}} + {{go}} Private shortlinks on your tailnet UTF-8 - http://{{.Hostname}}/.static/favicon.png - - http://{{.Hostname}}/ + http://{{go}}/.static/favicon.png + + http://{{go}}/ diff --git a/tmpl/success.html b/tmpl/success.html index 8659c6e..46507dc 100644 --- a/tmpl/success.html +++ b/tmpl/success.html @@ -1,5 +1,5 @@ {{ define "main" }}

Success

-

{{.Hostname}}/{{.Short}} has been saved.

+

{{go}}/{{.Short}} has been saved.

{{ end }}