From abdfbb704cef4f0d6bc4e5fec416d063806753a3 Mon Sep 17 00:00:00 2001 From: Neo Tan <10399087+neotan@users.noreply.github.com> Date: Fri, 2 Oct 2026 11:34:20 -0400 Subject: [PATCH] fix(web): bypass bare API aliases from embedded frontend fallback The embedded frontend middleware runs before the gateway routes and only let a few root aliases through. Bare aliases such as /chat/completions, /embeddings, /messages/count_tokens and /models/:model were answered with index.html and HTTP 200 instead of reaching the API handlers. Add every root alias registered in routes/gateway.go to the bypass list. This also covers POST /videos, which the /videos/ prefix did not match. Fixes #7670 --- backend/internal/web/embed_on.go | 16 ++++++++++++ backend/internal/web/embed_test.go | 40 ++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/backend/internal/web/embed_on.go b/backend/internal/web/embed_on.go index 516ac88dc0..d16b1c4d80 100644 --- a/backend/internal/web/embed_on.go +++ b/backend/internal/web/embed_on.go @@ -362,10 +362,26 @@ func shouldBypassEmbeddedFrontend(path string) bool { strings.HasPrefix(trimmed, "/setup/") || trimmed == "/health" || trimmed == "/models" || + strings.HasPrefix(trimmed, "/models/") || trimmed == "/responses" || strings.HasPrefix(trimmed, "/responses/") || + trimmed == "/chat/completions" || + trimmed == "/embeddings" || + trimmed == "/messages/count_tokens" || trimmed == "/alpha/search" || + trimmed == "/web_search" || + trimmed == "/x_search" || + trimmed == "/tts" || + trimmed == "/stt" || + trimmed == "/realtime" || + trimmed == "/custom-voices" || + strings.HasPrefix(trimmed, "/custom-voices/") || + trimmed == "/contents/generations/tasks" || + strings.HasPrefix(trimmed, "/contents/generations/tasks/") || + trimmed == "/v3/contents/generations/tasks" || + strings.HasPrefix(trimmed, "/v3/contents/generations/tasks/") || strings.HasPrefix(trimmed, "/images/") || + trimmed == "/videos" || strings.HasPrefix(trimmed, "/videos/") } diff --git a/backend/internal/web/embed_test.go b/backend/internal/web/embed_test.go index 3a9bc1151a..dc47269ff7 100644 --- a/backend/internal/web/embed_test.go +++ b/backend/internal/web/embed_test.go @@ -530,6 +530,9 @@ func TestFrontendServer_Middleware(t *testing.T) { "/health", "/responses", "/responses/compact", + "/chat/completions", + "/models/gpt-5.5", + "/v3/contents/generations/tasks/task-123", } for _, path := range apiPaths { @@ -689,6 +692,40 @@ func TestEmbeddedFrontendBypassesBareVideoAPIRoutes(t *testing.T) { } } +func TestEmbeddedFrontendBypassesBareAPIAliases(t *testing.T) { + for _, path := range []string{ + "/chat/completions", + "/embeddings", + "/messages/count_tokens", + "/models/gpt-5.5", + "/videos", + "/tts", + "/stt", + "/custom-voices", + "/custom-voices/voice-123", + "/custom-voices/voice-123/audio", + "/realtime", + "/web_search", + "/x_search", + "/contents/generations/tasks", + "/contents/generations/tasks/task-123", + "/v3/contents/generations/tasks", + "/v3/contents/generations/tasks/task-123", + } { + require.True(t, shouldBypassEmbeddedFrontend(path), "path=%s", path) + } + + for _, path := range []string{ + "/model-plaza", + "/custom/page-1", + "/monitor", + "/setup", + "/v3/other", + } { + require.False(t, shouldBypassEmbeddedFrontend(path), "path=%s", path) + } +} + func TestNewFrontendServer(t *testing.T) { t.Run("creates_server_successfully", func(t *testing.T) { provider := &mockSettingsProvider{ @@ -792,6 +829,9 @@ func TestServeEmbeddedFrontend(t *testing.T) { "/health", "/responses", "/responses/compact", + "/chat/completions", + "/models/gpt-5.5", + "/v3/contents/generations/tasks/task-123", } for _, path := range apiPaths {