File
Blob: ui/embed.go
| 1 | // Package ui serves the embedded client and operator frontends. |
| 2 | package ui |
| 3 | |
| 4 | import ( |
| 5 | "embed" |
| 6 | "fmt" |
| 7 | "io/fs" |
| 8 | "net/http" |
| 9 | "strings" |
| 10 | ) |
| 11 | |
| 12 | //go:embed all:dist |
| 13 | var output embed.FS |
| 14 | |
| 15 | func page(name string) http.Handler { |
| 16 | body, err := output.ReadFile("dist/" + name + "/index.html") |
| 17 | if err != nil { |
| 18 | panic(fmt.Errorf("read %s UI (run make ui): %w", name, err)) |
| 19 | } |
| 20 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 21 | if r.Method != http.MethodGet && r.Method != http.MethodHead { |
| 22 | w.Header().Set("Allow", "GET, HEAD") |
| 23 | http.Error(w, "method not allowed", http.StatusMethodNotAllowed) |
| 24 | return |
| 25 | } |
| 26 | w.Header().Set("Content-Type", "text/html; charset=utf-8") |
| 27 | w.Header().Set("Cache-Control", "no-cache") |
| 28 | if r.Method != http.MethodHead { |
| 29 | w.Write(body) |
| 30 | } |
| 31 | }) |
| 32 | } |
| 33 | |
| 34 | func ClientPage() http.Handler { return page("client") } |
| 35 | func OperatorPage() http.Handler { return page("operator") } |
| 36 | |
| 37 | // Assets serves common build assets relative to /ui/ or /_internal/ui/. |
| 38 | // The caller strips its prefix and applies the interface's access controls. |
| 39 | func Assets() http.Handler { |
| 40 | files, err := fs.Sub(output, "dist") |
| 41 | if err != nil { |
| 42 | panic(err) |
| 43 | } |
| 44 | server := http.FileServerFS(files) |
| 45 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 46 | name := strings.TrimPrefix(r.URL.Path, "/") |
| 47 | if !fs.ValidPath(name) || strings.HasSuffix(name, "/") || |
| 48 | (!strings.HasPrefix(name, "assets/") && !strings.HasPrefix(name, "fonts/")) { |
| 49 | http.NotFound(w, r) |
| 50 | return |
| 51 | } |
| 52 | if r.Method != http.MethodGet && r.Method != http.MethodHead { |
| 53 | w.Header().Set("Allow", "GET, HEAD") |
| 54 | http.Error(w, "method not allowed", http.StatusMethodNotAllowed) |
| 55 | return |
| 56 | } |
| 57 | w.Header().Set("Cache-Control", "private, max-age=31536000, immutable") |
| 58 | server.ServeHTTP(w, r) |
| 59 | }) |
| 60 | } |