// Package web embeds the built React SPA (web/dist, copied to // internal/web/dist by `make web` before go build/test) and serves it // with SPA-fallback: any non-file path resolves to index.html so // client-side routing works. package web import ( "embed" "io/fs" "net/http" "strings" ) //go:embed all:dist var distFS embed.FS // Handler serves the embedded SPA with fallback to index.html for // client-side routes (any non-file path that isn't an API route). func Handler() (http.Handler, error) { sub, err := fs.Sub(distFS, "dist") if err != nil { return nil, err } fileServer := http.FileServer(http.FS(sub)) index, err := fs.ReadFile(sub, "index.html") if err != nil { return nil, err } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // существующий файл (ассет) — отдать как есть p := strings.TrimPrefix(r.URL.Path, "/") if p != "" { if f, err := sub.Open(p); err == nil { _ = f.Close() fileServer.ServeHTTP(w, r) return } } // иначе — SPA index w.Header().Set("Content-Type", "text/html; charset=utf-8") _, _ = w.Write(index) }), nil }