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
This commit is contained in:
Neo Tan
2026-10-02 11:40:14 -04:00
parent b8dece9000
commit abdfbb704c
2 changed files with 56 additions and 0 deletions
+16
View File
@@ -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/")
}
+40
View File
@@ -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 {