(mux *http.ServeMux)
| 148 | } |
| 149 | |
| 150 | func (s *Server) mountStatic(mux *http.ServeMux) { |
| 151 | if s.config.Dev { |
| 152 | return |
| 153 | } |
| 154 | |
| 155 | staticFS, err := fs.Sub(StaticFiles(), "static/dist") |
| 156 | if err != nil { |
| 157 | s.mountFallback(mux) |
| 158 | return |
| 159 | } |
| 160 | |
| 161 | if _, err := staticFS.Open("index.html"); err != nil { |
| 162 | s.mountFallback(mux) |
| 163 | return |
| 164 | } |
| 165 | |
| 166 | fileServer := http.FileServer(http.FS(staticFS)) |
| 167 | |
| 168 | // Pre-render index.html with injected config to avoid layout shift |
| 169 | indexHTML := s.buildIndexHTML(staticFS) |
| 170 | |
| 171 | mux.HandleFunc("/{path...}", func(w http.ResponseWriter, r *http.Request) { |
| 172 | if len(r.URL.Path) >= 4 && r.URL.Path[:4] == "/api" { |
| 173 | http.NotFound(w, r) |
| 174 | return |
| 175 | } |
| 176 | |
| 177 | path := r.URL.Path |
| 178 | if path == "/" { |
| 179 | path = "/index.html" |
| 180 | } |
| 181 | |
| 182 | // Serve static assets normally |
| 183 | if path != "/index.html" { |
| 184 | f, err := staticFS.Open(path[1:]) |
| 185 | if err == nil { |
| 186 | f.Close() |
| 187 | fileServer.ServeHTTP(w, r) |
| 188 | return |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | // SPA fallback: serve index.html with injected config |
| 193 | w.Header().Set("Content-Type", "text/html; charset=utf-8") |
| 194 | _, _ = w.Write(indexHTML) |
| 195 | }) |
| 196 | } |
| 197 | |
| 198 | // buildIndexHTML reads index.html and injects the initial config as a script tag. |
| 199 | func (s *Server) buildIndexHTML(staticFS fs.FS) []byte { |
no test coverage detected