package httpserver import ( "io/fs" "net/http" ) // spaHandler serves the embedded Vue SPA out of distFS (already rooted at // web/dist, i.e. index.html is at its top level). Any request path that // doesn't match a real file falls back to index.html so client-side routes // (deep links) resolve correctly. func spaHandler(distFS fs.FS) http.HandlerFunc { fileServer := http.FileServer(http.FS(distFS)) return func(w http.ResponseWriter, r *http.Request) { upath := r.URL.Path if upath == "" || upath == "/" { upath = "index.html" } else { upath = upath[1:] // strip leading slash for fs.Stat } if _, err := fs.Stat(distFS, upath); err != nil { r = cloneWithPath(r, "/") } fileServer.ServeHTTP(w, r) } } // cloneWithPath returns a shallow copy of r with URL.Path replaced, so the // original request (and its URL) is left untouched. func cloneWithPath(r *http.Request, path string) *http.Request { r2 := r.Clone(r.Context()) r2.URL.Path = path return r2 }