client/web: combine embeds into a single embed.FS

instead of embedding each file individually, embed them all into a
single embed filesystem.  This is basically a noop for the current
frontend, but sets things up a little cleaner for the new frontend.

Also added an embed.FS for the source files needed to build the new
frontend. These files are not actually embedded into the binary (since
it is a blank identifier), but causes `go mod vendor` to copy them into
the vendor directory.

Updates tailscale/corp#13775

Signed-off-by: Will Norris <will@tailscale.com>
pull/9017/head
Will Norris 2 years ago committed by Will Norris
parent 3722b05465
commit 0df5507c81

@ -9,7 +9,7 @@ import (
"context" "context"
"crypto/rand" "crypto/rand"
"crypto/tls" "crypto/tls"
_ "embed" "embed"
"encoding/json" "encoding/json"
"encoding/xml" "encoding/xml"
"fmt" "fmt"
@ -36,16 +36,19 @@ import (
"tailscale.com/version/distro" "tailscale.com/version/distro"
) )
//go:embed web.html // This contains all files needed to build the frontend assets.
var webHTML string // Because we assign this to the blank identifier, it does not actually embed the files.
// However, this does cause `go mod vendor` to include the files when vendoring the package.
//go:embed web.css // External packages that use the web client can `go mod vendor`, run `yarn build` to
var webCSS string // build the assets, then those asset bundles will be able to be embedded.
//
//go:embed yarn.lock index.html *.js *.json src/**/*
var _ embed.FS
//go:embed auth-redirect.html //go:embed web.html web.css auth-redirect.html
var authenticationRedirectHTML string var embeddedFS embed.FS
var tmpl *template.Template var tmpls *template.Template
// Server is the backend server for a Tailscale web client. // Server is the backend server for a Tailscale web client.
type Server struct { type Server struct {
@ -84,8 +87,7 @@ func NewServer(devMode bool, lc *tailscale.LocalClient) (s *Server, cleanup func
} }
func init() { func init() {
tmpl = template.Must(template.New("web.html").Parse(webHTML)) tmpls = template.Must(template.New("").ParseFS(embeddedFS, "*"))
template.Must(tmpl.New("web.css").Parse(webCSS))
} }
// authorize returns the name of the user accessing the web UI after verifying // authorize returns the name of the user accessing the web UI after verifying
@ -301,7 +303,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch { switch {
case r.URL.Path == "/redirect" || r.URL.Path == "/redirect/": case r.URL.Path == "/redirect" || r.URL.Path == "/redirect/":
io.WriteString(w, authenticationRedirectHTML) if err := tmpls.ExecuteTemplate(w, "auth-redirect.html", nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return return
case r.Method == "POST": case r.Method == "POST":
s.servePostNodeUpdate(w, r) s.servePostNodeUpdate(w, r)
@ -380,7 +384,7 @@ func (s *Server) serveGetNodeData(w http.ResponseWriter, r *http.Request, user s
return return
} }
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
if err := tmpl.Execute(buf, *data); err != nil { if err := tmpls.ExecuteTemplate(buf, "web.html", data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }

Loading…
Cancel
Save