//go:build embedspa // spa_embed.go — serves the Vite-built SPA embedded into the binary. Built only // with `-tags embedspa` (after `npm --prefix ui ci && npm --prefix ui run build`), // so the default `go build`/`go test` don't require ui/dist (a build artifact). // The Dockerfile builds the SPA then compiles with this tag. package main import ( "embed" "io/fs" "net/http" "strings" ) //go:embed all:ui/dist var spaFiles embed.FS func (s *server) spaHandler() http.Handler { sub, err := fs.Sub(spaFiles, "ui/dist") if err != nil { panic(err) } files := http.FileServer(http.FS(sub)) return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { p := strings.TrimPrefix(r.URL.Path, "/") if p == "" { p = "index.html" } // Real asset -> serve it; anything else -> SPA entry (hash routing). if _, err := fs.Stat(sub, p); err != nil { http.ServeFileFS(w, r, sub, "index.html") return } files.ServeHTTP(w, r) }) }