diff --git a/cmd/gitops-server/cmd/cmd.go b/cmd/gitops-server/cmd/cmd.go
index 81dbc262c1..a4f2e0bb5f 100644
--- a/cmd/gitops-server/cmd/cmd.go
+++ b/cmd/gitops-server/cmd/cmd.go
@@ -11,7 +11,6 @@ import (
"os"
"os/signal"
"path"
- "path/filepath"
"strings"
"syscall"
"time"
@@ -157,9 +156,6 @@ func runCmd(cmd *cobra.Command, args []string) error {
}
}))
- assetFS := getAssets()
- assetHandler := http.FileServer(http.FS(assetFS))
- redirector := createRedirector(assetFS, log, options.RoutePrefix)
clusterName := kube.InClusterConfigClusterName()
rest, err := config.GetConfig()
@@ -271,18 +267,12 @@ func runCmd(cmd *cobra.Command, args []string) error {
mux.Handle("/v1/", gziphandler.GzipHandler(appAndProfilesHandlers))
- mux.Handle("/", gziphandler.GzipHandler(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- // Assume anything with a file extension in the name is a static asset.
- extension := filepath.Ext(req.URL.Path)
- // We use the golang http.FileServer for static file requests.
- // This will return a 404 on normal page requests, ie /some-page.
- // Redirect all non-file requests to index.html, where the JS routing will take over.
- if extension == "" {
- redirector(w, req)
- return
- }
- assetHandler.ServeHTTP(w, req)
- })))
+ // Static asset handling
+ assetFS := getAssets()
+ assertFSHandler := http.FileServer(http.FS(assetFS))
+ redirectHandler := server.IndexHTMLHandler(assetFS, log, options.RoutePrefix)
+ assetHandler := server.AssetHandler(assertFSHandler, redirectHandler)
+ mux.Handle("/", gziphandler.GzipHandler(assetHandler))
if options.RoutePrefix != "" {
mux = server.WithRoutePrefix(mux, options.RoutePrefix)
@@ -405,51 +395,3 @@ func getAssets() fs.FS {
return f
}
-
-// A redirector ensures that index.html always gets served.
-// The JS router will take care of actual navigation once the index.html page lands.
-func createRedirector(fsys fs.FS, log logr.Logger, routePrefix string) http.HandlerFunc {
- baseHref := server.GetBaseHref(routePrefix)
- log.Info("Creating redirector", "routePrefix", routePrefix, "baseHref", baseHref)
-
- return func(w http.ResponseWriter, r *http.Request) {
- indexPage, err := fsys.Open("index.html")
-
- if err != nil {
- log.Error(err, "could not open index.html page")
- w.WriteHeader(http.StatusInternalServerError)
-
- return
- }
-
- stat, err := indexPage.Stat()
- if err != nil {
- log.Error(err, "could not get index.html stat")
- w.WriteHeader(http.StatusInternalServerError)
-
- return
- }
-
- bt := make([]byte, stat.Size())
- _, err = indexPage.Read(bt)
-
- if err != nil {
- log.Error(err, "could not read index.html")
- w.WriteHeader(http.StatusInternalServerError)
-
- return
- }
-
- // inject base tag into index.html
- bt = server.InjectHTMLBaseTag(bt, baseHref)
-
- _, err = w.Write(bt)
-
- if err != nil {
- log.Error(err, "error writing index.html")
- w.WriteHeader(http.StatusInternalServerError)
-
- return
- }
- }
-}
diff --git a/pkg/server/static_assets.go b/pkg/server/static_assets.go
new file mode 100644
index 0000000000..8652e8fd22
--- /dev/null
+++ b/pkg/server/static_assets.go
@@ -0,0 +1,61 @@
+package server
+
+import (
+ "io"
+ "io/fs"
+ "net/http"
+ "path/filepath"
+
+ "github.com/go-logr/logr"
+)
+
+// AssetHandler returns a http.Handler that serves static assets from the provided fs.FS.
+// It also redirects all non-file requests to index.html.
+func AssetHandler(assetHandler, redirectHandler http.Handler) http.HandlerFunc {
+ return func(w http.ResponseWriter, req *http.Request) {
+ // Assume anything with a file extension in the name is a static asset.
+ extension := filepath.Ext(req.URL.Path)
+ // We use the golang http.FileServer for static file requests.
+ // This will return a 404 on normal page requests, ie /some-page.
+ // Redirect all non-file requests to index.html, where the JS routing will take over.
+ if extension == "" {
+ redirectHandler.ServeHTTP(w, req)
+ return
+ }
+ assetHandler.ServeHTTP(w, req)
+ }
+}
+
+// IndexHTMLHandler ensures that index.html always gets served.
+// The JS router will take care of actual navigation once the index.html page lands.
+func IndexHTMLHandler(fsys fs.FS, log logr.Logger, routePrefix string) http.HandlerFunc {
+ baseHref := GetBaseHref(routePrefix)
+ log.Info("Creating redirector", "routePrefix", routePrefix, "baseHref", baseHref)
+
+ return func(w http.ResponseWriter, r *http.Request) {
+ indexPage, err := fsys.Open("index.html")
+ if err != nil {
+ log.Error(err, "could not open index.html page")
+ http.Error(w, "could not open index.html page", http.StatusInternalServerError)
+ return
+ }
+ defer indexPage.Close()
+
+ bt, err := io.ReadAll(indexPage)
+ if err != nil {
+ log.Error(err, "could not read index.html")
+ http.Error(w, "could not read index.html", http.StatusInternalServerError)
+ return
+ }
+
+ // inject base tag into index.html
+ bt = InjectHTMLBaseTag(bt, baseHref)
+
+ _, err = w.Write(bt)
+ if err != nil {
+ log.Error(err, "error writing index.html")
+ http.Error(w, "error writing index.html", http.StatusInternalServerError)
+ return
+ }
+ }
+}
diff --git a/pkg/server/static_assets_test.go b/pkg/server/static_assets_test.go
new file mode 100644
index 0000000000..84d691c075
--- /dev/null
+++ b/pkg/server/static_assets_test.go
@@ -0,0 +1,118 @@
+package server
+
+import (
+ "io"
+ "io/fs"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "strings"
+ "testing"
+
+ "github.com/go-logr/logr"
+)
+
+func TestCreateRedirector(t *testing.T) {
+ log := logr.Discard()
+ // Easiest way to create a filesystem..
+ fsys, err := fs.Sub(os.DirFS("testdata"), "public")
+ if err != nil {
+ t.Fatalf("failed to create fs: %v", err)
+ }
+
+ t.Run("We read the index.html and inject base", func(t *testing.T) {
+ req := httptest.NewRequest("GET", "http://example.com/foo", nil)
+ w := httptest.NewRecorder()
+ handler := IndexHTMLHandler(fsys, log, "/prefix")
+
+ handler.ServeHTTP(w, req)
+
+ resp := w.Result()
+ body, _ := io.ReadAll(resp.Body)
+
+ // Check the status code
+ if resp.StatusCode != http.StatusOK {
+ t.Errorf("expected status OK; got %v", resp.StatusCode)
+ }
+
+ // Check that the base tag was injected
+ if !strings.Contains(string(body), `