mirror of
https://github.com/gesellix/Bose-SoundTouch.git
synced 2026-08-19 00:56:16 +00:00
refactor(web): serve the SPA under /app/* (refs #451)
Move the soundtouch-web single-page app from top-level page paths (/devices, /tunein, ...) under one /app subtree, so the whole web UI lives under /app/* and folding -web into -service stays an additive mount. The client navigates via component state rather than the URL and all assets are referenced absolutely (/static/...), so this is a pure routing change: no frontend edits needed. The bare root / now redirects into the app (standalone convenience). When -web is folded into -service, / instead serves a landing page (admin vs app) and this redirect is replaced. Extend mount_test.go with TestMountSPARoutes: the SPA resolves under /app, the old top-level page paths are gone, and / remains only as the redirect. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
a16b4babcb
commit
cd47ae0c5a
@@ -99,14 +99,25 @@ func (app *WebApp) Mount(r chi.Router, discoveryService *discovery.UnifiedDiscov
|
||||
})
|
||||
})
|
||||
|
||||
// SPA routes — serve index.html for client-side routing
|
||||
r.Get("/", app.serveIndex)
|
||||
r.Get("/devices", app.serveIndex)
|
||||
r.Get("/device/*", app.serveIndex)
|
||||
r.Get("/tunein", app.serveIndex)
|
||||
r.Get("/radiobrowser", app.serveIndex)
|
||||
r.Get("/playurl", app.serveIndex)
|
||||
r.Get("/tts", app.serveIndex)
|
||||
// SPA — served under /app/*. The client navigates via component state
|
||||
// rather than the URL, so these entries only ensure deep links and
|
||||
// refreshes return index.html instead of 404. Per #451 this keeps the
|
||||
// whole web UI under one /app subtree, so folding -web into -service is an
|
||||
// additive mount.
|
||||
r.Get("/app", app.serveIndex)
|
||||
r.Get("/app/devices", app.serveIndex)
|
||||
r.Get("/app/device/*", app.serveIndex)
|
||||
r.Get("/app/tunein", app.serveIndex)
|
||||
r.Get("/app/radiobrowser", app.serveIndex)
|
||||
r.Get("/app/playurl", app.serveIndex)
|
||||
r.Get("/app/tts", app.serveIndex)
|
||||
|
||||
// Standalone convenience: the bare root jumps into the app. When -web is
|
||||
// folded into -service, / instead serves a landing page (admin vs app) and
|
||||
// this redirect is replaced.
|
||||
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/app", http.StatusFound)
|
||||
})
|
||||
}
|
||||
|
||||
func (app *WebApp) serveIndex(w http.ResponseWriter, _ *http.Request) {
|
||||
|
||||
@@ -59,3 +59,42 @@ func TestMountControlAPIShape(t *testing.T) {
|
||||
t.Errorf("expected /api/control/version to be registered; got %v", apiRoutes)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMountSPARoutes verifies the issue #451 SPA move: the web UI is served
|
||||
// under /app/* and the old top-level page paths are gone, with / kept only as
|
||||
// a redirect into the app.
|
||||
func TestMountSPARoutes(t *testing.T) {
|
||||
app := NewWebApp()
|
||||
|
||||
r := chi.NewRouter()
|
||||
app.Mount(r, nil)
|
||||
|
||||
routes := map[string]bool{}
|
||||
|
||||
walkErr := chi.Walk(r, func(_, route string, _ http.Handler, _ ...func(http.Handler) http.Handler) error {
|
||||
routes[route] = true
|
||||
|
||||
return nil
|
||||
})
|
||||
if walkErr != nil {
|
||||
t.Fatalf("walk routes: %v", walkErr)
|
||||
}
|
||||
|
||||
for _, want := range []string{"/app", "/app/devices", "/app/tunein"} {
|
||||
if !routes[want] {
|
||||
t.Errorf("expected SPA route %q under /app to be registered", want)
|
||||
}
|
||||
}
|
||||
|
||||
// The old top-level page paths moved under /app.
|
||||
for _, gone := range []string{"/devices", "/device/*", "/tunein", "/radiobrowser", "/playurl", "/tts"} {
|
||||
if routes[gone] {
|
||||
t.Errorf("top-level SPA route %q should have moved under /app", gone)
|
||||
}
|
||||
}
|
||||
|
||||
// / stays registered, but only as the redirect into the app.
|
||||
if !routes["/"] {
|
||||
t.Error("expected / to remain registered (redirect into the app)")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user