44 lines
1.1 KiB
Go
44 lines
1.1 KiB
Go
// 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
|
|
}
|