feat(spa): add SPA Go package with embedded dist FS
Creates internal/spa package that: - Embeds React build output from cmd/kafka/dist/ - Provides HTTP handler for static file serving - Falls back to index.html for SPA client-side routing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1543b16605
commit
8651183540
1 changed files with 56 additions and 0 deletions
56
internal/spa/spa.go
Normal file
56
internal/spa/spa.go
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
package spa
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"io/fs"
|
||||||
|
"net/http"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed all:dist
|
||||||
|
var distFS embed.FS
|
||||||
|
|
||||||
|
// DistFS returns the embedded dist directory as an fs.FS.
|
||||||
|
func DistFS() (fs.FS, error) {
|
||||||
|
return fs.Sub(distFS, "dist")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHandler returns an HTTP handler that:
|
||||||
|
// - Serves static files from the embedded dist/ directory
|
||||||
|
// - Falls back to index.html for SPA routing (any non-API path)
|
||||||
|
func NewHandler() http.Handler {
|
||||||
|
dist, err := DistFS()
|
||||||
|
if err != nil {
|
||||||
|
panic("spa: embedded dist not found: " + err.Error())
|
||||||
|
}
|
||||||
|
return &spaHandler{dist: dist}
|
||||||
|
}
|
||||||
|
|
||||||
|
type spaHandler struct {
|
||||||
|
dist fs.FS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *spaHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// API paths are handled by Go API handlers - this should never be reached
|
||||||
|
// since Go mux dispatches to specific handlers first. But if reached,
|
||||||
|
// pass through to FileServer which will return 404 for unknown paths.
|
||||||
|
|
||||||
|
// Try to serve the requested file first
|
||||||
|
filePath := path.Clean(r.URL.Path)
|
||||||
|
f, err := h.dist.Open(filePath)
|
||||||
|
if err == nil {
|
||||||
|
f.Close()
|
||||||
|
// File exists - serve it via FileServer
|
||||||
|
http.FileServer(http.FS(h.dist)).ServeHTTP(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to index.html for SPA routing
|
||||||
|
indexFile, err := h.dist.Open("index.html")
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "index.html not found in embedded files", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
indexFile.Close()
|
||||||
|
http.FileServer(http.FS(h.dist)).ServeHTTP(w, r)
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue