phase 0 complete

This commit is contained in:
2026-08-14 11:29:11 -06:00
parent 4da16c7573
commit 85df9a208e
102 changed files with 25019 additions and 30 deletions
+37
View File
@@ -0,0 +1,37 @@
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
}