core/ui: fix page title (#4957)

* core/ui: fix page title

* cache template
This commit is contained in:
Caleb Doxsey 2024-02-12 14:05:18 -07:00 committed by GitHub
parent 76862c2fe8
commit c6d1f17100
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 56 additions and 31 deletions

View file

@ -10,6 +10,6 @@ import (
// DeviceEnrolled displays an HTML page informing the user that they've successfully enrolled a device.
func DeviceEnrolled(data UserInfoData) http.Handler {
return httputil.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
return ui.ServePage(w, r, "DeviceEnrolled", data.ToJSON())
return ui.ServePage(w, r, "DeviceEnrolled", "Device Enrolled", data.ToJSON())
})
}

View file

@ -24,6 +24,6 @@ func SignedOut(data SignedOutData) http.Handler {
}
// otherwise show the signed-out page
return ui.ServePage(w, r, "SignedOut", data.ToJSON())
return ui.ServePage(w, r, "SignedOut", "Signed Out", data.ToJSON())
})
}

View file

@ -22,6 +22,6 @@ func (data SignOutConfirmData) ToJSON() map[string]interface{} {
// SignOutConfirm returns a handler that renders the sign out confirm page.
func SignOutConfirm(data SignOutConfirmData) http.Handler {
return httputil.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
return ui.ServePage(w, r, "SignOutConfirm", data.ToJSON())
return ui.ServePage(w, r, "SignOutConfirm", "Confirm Sign Out", data.ToJSON())
})
}

View file

@ -65,6 +65,6 @@ func (data UserInfoData) ToJSON() map[string]any {
// UserInfo returns a handler that renders the user info page.
func UserInfo(data UserInfoData) http.Handler {
return httputil.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error {
return ui.ServePage(w, r, "UserInfo", data.ToJSON())
return ui.ServePage(w, r, "UserInfo", "User Info Dashboard", data.ToJSON())
})
}

View file

@ -402,7 +402,7 @@ func (h *Handler) handleView(w http.ResponseWriter, r *http.Request, state *Stat
"selfUrl": r.URL.String(),
}
httputil.AddBrandingOptionsToMap(m, state.BrandingOptions)
return ui.ServePage(w, r, "WebAuthnRegistration", m)
return ui.ServePage(w, r, "WebAuthnRegistration", "Device Registration", m)
}
func (h *Handler) saveSessionAndRedirect(w http.ResponseWriter, r *http.Request, state *State, rawRedirectURI string) error {

View file

@ -2,6 +2,7 @@ package httputil
import (
"context"
"fmt"
"net/http"
"net/url"
@ -101,7 +102,7 @@ func (e *HTTPError) ErrorResponse(ctx context.Context, w http.ResponseWriter, r
w.Header().Set("Content-Type", "text/html; charset=UTF-8")
w.WriteHeader(response.Status)
if err := ui.ServePage(w, r, "Error", m); err != nil {
if err := ui.ServePage(w, r, "Error", fmt.Sprintf("%d %s", response.Status, response.StatusText), m); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

View file

@ -25,14 +25,14 @@
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<title>User info dashboard</title>
<title>{{.Title}}</title>
<link rel="stylesheet" href="/.pomerium/index.css" />
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script>
window.POMERIUM_DATA = {};
window.POMERIUM_DATA = {{.Data}};
</script>
<script src="/.pomerium/index.js"></script>
</body>

View file

@ -3,10 +3,12 @@ package ui
import (
"bytes"
"encoding/json"
"html/template"
"io"
"io/fs"
"net/http"
"path/filepath"
"sync"
"time"
"github.com/pomerium/csrf"
@ -26,35 +28,59 @@ func ServeFile(w http.ResponseWriter, r *http.Request, filePath string) error {
}
// ServePage serves the index.html page.
func ServePage(w http.ResponseWriter, r *http.Request, page string, data map[string]interface{}) error {
func ServePage(w http.ResponseWriter, r *http.Request, page, title string, data map[string]interface{}) error {
if data == nil {
data = make(map[string]interface{})
data = make(map[string]any)
}
data["csrfToken"] = csrf.Token(r)
data["page"] = page
jsonData, err := json.Marshal(data)
bs, err := renderIndex(map[string]any{
"Title": title,
"Data": data,
})
if err != nil {
return err
}
f, _, err := openFile("dist/index.html")
if err != nil {
return err
}
bs, err := io.ReadAll(f)
_ = f.Close()
if err != nil {
return err
}
bs = bytes.Replace(bs,
[]byte("window.POMERIUM_DATA = {}"),
append([]byte("window.POMERIUM_DATA = "), jsonData...),
1)
http.ServeContent(w, r, "index.html", time.Now(), bytes.NewReader(bs))
return nil
}
var startTime = time.Now()
func renderIndex(data any) ([]byte, error) {
tpl, err := parseIndex()
if err != nil {
return nil, err
}
var buf bytes.Buffer
err = tpl.Execute(&buf, data)
return buf.Bytes(), err
}
var (
parseIndexOnce sync.Once
parseIndexTemplate *template.Template
parseIndexError error
)
func parseIndex() (*template.Template, error) {
parseIndexOnce.Do(func() {
var f fs.File
f, _, parseIndexError = openFile("dist/index.gohtml")
if parseIndexError != nil {
return
}
var bs []byte
bs, parseIndexError = io.ReadAll(f)
_ = f.Close()
if parseIndexError != nil {
return
}
parseIndexTemplate, parseIndexError = template.New("").Parse(string(bs))
})
return parseIndexTemplate, parseIndexError
}

View file

@ -7,10 +7,8 @@ import (
"io/fs"
)
var (
// ExtUIFS must be set to provide access to UI dist/ files
ExtUIFS fs.FS
)
// ExtUIFS must be set to provide access to UI dist/ files
var ExtUIFS fs.FS
func openFile(name string) (f fs.File, etag string, err error) {
if ExtUIFS == nil {