diff --git a/internal/router/router_static_test.go b/internal/router/router_static_test.go index f8d7a1a984..0dacb8e6ec 100644 --- a/internal/router/router_static_test.go +++ b/internal/router/router_static_test.go @@ -11,6 +11,68 @@ import ( "github.com/stretchr/testify/require" ) +func TestFrontendStaticServesEmbedEntryForEmbedRoute(t *testing.T) { + gin.SetMode(gin.TestMode) + webDir := t.TempDir() + require.NoError(t, os.WriteFile(filepath.Join(webDir, "index.html"), []byte("main spa"), 0o600)) + require.NoError(t, os.WriteFile(filepath.Join(webDir, "embed.html"), []byte("embed spa"), 0o600)) + t.Setenv("WEKNORA_WEB_DIR", webDir) + + r := gin.New() + serveFrontendStatic(r) + + for _, method := range []string{http.MethodGet, http.MethodHead} { + t.Run(method, func(t *testing.T) { + recorder := httptest.NewRecorder() + request := httptest.NewRequest(method, "/embed/channel-123?locale=zh-CN", nil) + r.ServeHTTP(recorder, request) + + require.Equal(t, http.StatusOK, recorder.Code) + require.Equal(t, "no-cache, must-revalidate", recorder.Header().Get("Cache-Control")) + if method == http.MethodGet { + require.Equal(t, "embed spa", recorder.Body.String()) + } else { + require.Empty(t, recorder.Body.String()) + } + }) + } +} + +func TestFrontendStaticKeepsMainSPAFallbackForNonEmbedRoute(t *testing.T) { + gin.SetMode(gin.TestMode) + webDir := t.TempDir() + require.NoError(t, os.WriteFile(filepath.Join(webDir, "index.html"), []byte("main spa"), 0o600)) + require.NoError(t, os.WriteFile(filepath.Join(webDir, "embed.html"), []byte("embed spa"), 0o600)) + t.Setenv("WEKNORA_WEB_DIR", webDir) + + r := gin.New() + serveFrontendStatic(r) + + recorder := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, "/settings/profile", nil) + r.ServeHTTP(recorder, request) + + require.Equal(t, http.StatusOK, recorder.Code) + require.Equal(t, "main spa", recorder.Body.String()) +} + +func TestFrontendStaticReturnsNotFoundWhenEmbedEntryIsMissing(t *testing.T) { + gin.SetMode(gin.TestMode) + webDir := t.TempDir() + require.NoError(t, os.WriteFile(filepath.Join(webDir, "index.html"), []byte("main spa"), 0o600)) + t.Setenv("WEKNORA_WEB_DIR", webDir) + + r := gin.New() + serveFrontendStatic(r) + + recorder := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, "/embed/channel-123", nil) + r.ServeHTTP(recorder, request) + + require.Equal(t, http.StatusNotFound, recorder.Code) + require.NotContains(t, recorder.Body.String(), "main spa") +} + func TestFrontendStaticDoesNotInterceptResourceGrant(t *testing.T) { gin.SetMode(gin.TestMode) webDir := t.TempDir() diff --git a/internal/router/static.go b/internal/router/static.go index 0f496a2197..4b31e8fdd8 100644 --- a/internal/router/static.go +++ b/internal/router/static.go @@ -25,6 +25,12 @@ func serveFrontendStatic(r *gin.Engine) { if _, err := os.Stat(indexPath); err != nil { return } + embedIndexPath := filepath.Join(absDir, "embed.html") + embedIndexInfo, embedIndexErr := os.Stat(embedIndexPath) + hasEmbedIndex := embedIndexErr == nil && !embedIndexInfo.IsDir() + if !hasEmbedIndex { + logger.Warnf(context.Background(), "[Router] Embed entry %s is unavailable; /embed/* requests will return 404", embedIndexPath) + } logger.Infof(context.Background(), "[Router] Serving frontend static files from %s", absDir) @@ -49,6 +55,17 @@ func serveFrontendStatic(r *gin.Engine) { c.Abort() return } + if strings.HasPrefix(path, "/embed/") { + if !hasEmbedIndex { + c.Status(http.StatusNotFound) + c.Abort() + return + } + setFrontendCacheHeaders(c.Writer, "/embed.html") + c.File(embedIndexPath) + c.Abort() + return + } setFrontendCacheHeaders(c.Writer, "/index.html") c.File(indexPath) c.Abort()