diff --git a/cmd/server_foreground.go b/cmd/server_foreground.go index 3fd6bdc5c..640b517f2 100644 --- a/cmd/server_foreground.go +++ b/cmd/server_foreground.go @@ -21,6 +21,7 @@ import ( "encoding/json" "fmt" "io" + "io/fs" "log" "log/slog" "os" @@ -330,7 +331,14 @@ func runServerStart(cmd *cobra.Command, args []string) error { } if !web.AssetsEmbedded && webAssetsDir == "" { - slog.Warn("This binary was built without web assets. The web UI will not be available. Run 'make web' and rebuild to include the web frontend, or use --web-assets-dir.") + slog.Warn("This binary was built without web assets. The web UI will not be available. Run 'make all' (or 'make web && make build') to rebuild with web assets included, or use --web-assets-dir.") + } else if web.AssetsEmbedded && webAssetsDir == "" { + sub, err := fs.Sub(web.ClientAssets, "dist/client") + if err != nil { + slog.Error("Failed to create sub-filesystem from embedded assets. The web UI will not be available. Run 'make web && make build' to rebuild with web assets included, or use --web-assets-dir.", "error", err) + } else if _, err := fs.Stat(sub, "assets/main.js"); err != nil { + slog.Warn("Embedded web assets are incomplete (main.js missing). The web UI will not be available. Run 'make all' (or 'make web && make build') to rebuild with web assets included, or use --web-assets-dir.") + } } log.Printf("Starting Web Frontend on %s:%d", cfg.Hub.Host, webPort) wg.Add(1) diff --git a/pkg/hub/web.go b/pkg/hub/web.go index c1f95facc..de8d66512 100644 --- a/pkg/hub/web.go +++ b/pkg/hub/web.go @@ -381,11 +381,11 @@ var noAssetsPage = `

Web UI Not Available

-

This Scion binary was not built from source with web assets included. +

This hub binary was built without embedded web assets. The Hub API is still fully operational.

To use the web UI, either:

-

1. Build from source: make build

+

1. Rebuild with assets: make all (or make web && make build)

2. Point to pre-built assets: --web-assets-dir /path/to/dist/client

@@ -475,11 +475,15 @@ func NewWebServer(cfg WebServerConfig) *WebServer { } else if web.AssetsEmbedded { sub, err := fs.Sub(web.ClientAssets, "dist/client") if err != nil { - slog.Error("Failed to create sub-filesystem from embedded assets", "error", err) + slog.Error("Failed to create sub-filesystem from embedded assets. Run 'make web && make build' to rebuild with web assets included, or use --web-assets-dir.", "error", err) + } else if _, err := fs.Stat(sub, "assets/main.js"); err != nil { + slog.Warn("Embedded web assets directory exists but main.js is missing. Run 'make all' (or 'make web && make build') to rebuild with web assets included, or use --web-assets-dir.") } else { ws.assets = sub } - slog.Info("Web server using embedded assets") + if ws.assets != nil { + slog.Info("Web server using embedded assets") + } } else { slog.Warn("No web assets available: build with embedded assets or use --web-assets-dir") } diff --git a/pkg/hub/web_test.go b/pkg/hub/web_test.go index c9b4d1a4f..b929e4e8a 100644 --- a/pkg/hub/web_test.go +++ b/pkg/hub/web_test.go @@ -24,6 +24,7 @@ import ( "path/filepath" "strings" "testing" + "testing/fstest" "time" "github.com/GoogleCloudPlatform/scion/pkg/store" @@ -39,7 +40,13 @@ type mockWebStore struct { func newTestWebServer(t *testing.T, cfg WebServerConfig) *WebServer { t.Helper() - return NewWebServer(cfg) + ws := NewWebServer(cfg) + if ws.assets == nil && ws.assetsDisk == "" && cfg.AssetsDir == "" { + ws.assets = fstest.MapFS{ + "assets/main.js": &fstest.MapFile{Data: []byte("// test stub")}, + } + } + return ws } // newDevAuthWebServer creates a web server with dev-auth enabled for testing @@ -52,7 +59,13 @@ func newDevAuthWebServer(t *testing.T, overrides ...func(*WebServerConfig)) *Web for _, fn := range overrides { fn(&cfg) } - return NewWebServer(cfg) + ws := NewWebServer(cfg) + if ws.assets == nil && ws.assetsDisk == "" { + ws.assets = fstest.MapFS{ + "assets/main.js": &fstest.MapFile{Data: []byte("// test stub")}, + } + } + return ws } func TestSPAShellHandler(t *testing.T) { @@ -231,7 +244,7 @@ func TestSPAHandler_NoAssets_ServesErrorPage(t *testing.T) { assert.Equal(t, http.StatusOK, resp.StatusCode) assert.Contains(t, resp.Header.Get("Content-Type"), "text/html") assert.Contains(t, html, "Web UI Not Available") - assert.Contains(t, html, "not built from source") + assert.Contains(t, html, "built without embedded web assets") assert.Contains(t, html, "Hub API") assert.NotContains(t, html, "scion-app", "should not render SPA shell when no assets are available")