package webui import ( "io/fs" "mime" "net/http" "path" "strconv" "strings" ) // Handler отдаёт статику SvelteKit (embed) и index.html для клиентских маршрутов SPA. func Handler() http.Handler { root, err := fs.Sub(static, "static") if err != nil { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.NotFound(w, r) }) } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet && r.Method != http.MethodHead { http.Error(w, "method not allowed", http.StatusMethodNotAllowed) return } name := strings.TrimPrefix(path.Clean(r.URL.Path), "/") if name == "." || name == "" { name = "index.html" } b, err := fs.ReadFile(root, name) if err != nil { if path.Ext(name) != "" { http.NotFound(w, r) return } b, err = fs.ReadFile(root, "index.html") if err != nil { http.NotFound(w, r) return } name = "index.html" } ct := mime.TypeByExtension(path.Ext(name)) if ct == "" { ct = "application/octet-stream" } if strings.HasPrefix(ct, "text/") && !strings.Contains(ct, "charset") { ct = ct + "; charset=utf-8" } w.Header().Set("Content-Type", ct) if name == "index.html" { w.Header().Set("Cache-Control", "no-cache") } else { w.Header().Set("Cache-Control", "public, max-age=31536000, immutable") } if r.Method == http.MethodHead { w.Header().Set("Content-Length", strconv.Itoa(len(b))) w.WriteHeader(http.StatusOK) return } w.WriteHeader(http.StatusOK) _, _ = w.Write(b) }) }