// Package ui contains the user info dashboard ui. package ui import ( "bytes" "embed" "encoding/json" "fmt" "io" "io/fs" "net/http" "os" "path/filepath" "time" "github.com/pomerium/csrf" ) var ( //go:embed dist/* uiFS embed.FS ) // ServeFile serves a file. func ServeFile(w http.ResponseWriter, r *http.Request, filePath string) error { f, etag, err := openLocalOrEmbeddedFile(filepath.Join("dist", filePath)) if err != nil { return err } defer f.Close() w.Header().Set("ETag", `"`+etag+`"`) http.ServeContent(w, r, filepath.Base(filePath), time.Time{}, f.(io.ReadSeeker)) return nil } // ServePage serves the index.html page. func ServePage(w http.ResponseWriter, r *http.Request, page string, data map[string]interface{}) error { if data == nil { data = make(map[string]interface{}) } data["csrfToken"] = csrf.Token(r) data["page"] = page jsonData, err := json.Marshal(data) if err != nil { return err } f, _, err := openLocalOrEmbeddedFile("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 openLocalOrEmbeddedFile(name string) (f fs.File, etag string, err error) { f, err = os.Open(filepath.Join("ui", name)) if os.IsNotExist(err) { f, err = uiFS.Open(name) } if err != nil { return nil, "", err } fi, err := f.Stat() if err != nil { _ = f.Close() return nil, "", err } modTime := fi.ModTime() if modTime.IsZero() { modTime = startTime } etag = fmt.Sprintf("%x", modTime.UnixNano()) return f, etag, nil }