package static import ( "embed" "io/fs" "mime" "net/http" "path/filepath" "strings" "github.com/gin-gonic/gin" ) type Handler struct { content embed.FS } func New(content embed.FS) *Handler { return &Handler{content: content} } func (h *Handler) Register(r *gin.Engine) { sub, err := fs.Sub(h.content, "dist") if err != nil { sub = h.content } r.NoRoute(func(c *gin.Context) { if strings.HasPrefix(c.Request.URL.Path, "/api/") || strings.HasPrefix(c.Request.URL.Path, "/v1/") { c.JSON(http.StatusNotFound, gin.H{"error": "not found"}) return } h.serve(c, sub) }) r.GET("/", func(c *gin.Context) { h.serve(c, sub) }) r.GET("/assets/*filepath", func(c *gin.Context) { h.serve(c, sub) }) } func (h *Handler) serve(c *gin.Context, sub fs.FS) { path := strings.TrimPrefix(c.Request.URL.Path, "/") if path == "" { path = "index.html" } data, err := fs.ReadFile(sub, path) if err != nil { data, err = fs.ReadFile(sub, "index.html") if err != nil { c.String(http.StatusNotFound, "UI not built. Run: make build-web") return } path = "index.html" } ext := filepath.Ext(path) ct := mime.TypeByExtension(ext) if ct == "" { ct = "text/html" } if strings.HasPrefix(path, "assets/") { c.Header("Cache-Control", "public, max-age=31536000, immutable") } else { c.Header("Cache-Control", "no-cache") } c.Data(http.StatusOK, ct, data) }