Files
artex/server/webui_embed.go
T
dela 0335d572de
ci / go (push) Waiting to run
ci / go-db (agent) (push) Waiting to run
ci / go-db (config) (push) Waiting to run
ci / go-db (db) (push) Waiting to run
ci / go-db (evidence) (push) Waiting to run
ci / go-db (llmrec) (push) Waiting to run
ci / go-db (server) (push) Waiting to run
web / web (push) Waiting to run
docs / links (push) Canceled after 0s
detections / detections (push) Canceled after 0s
First Commit
2026-10-09 08:38:16 +08:00

61 lines
1.7 KiB
Go

//go:build embedui
package server
import (
"embed"
"io/fs"
"net/http"
"path"
"strings"
)
// webuiDist holds the statically-exported frontend (built with
// `cd web && npm run build:static`, then copied into server/webui/dist). Compiled
// into the binary only when building with `-tags embedui`. The `all:` prefix is
// required so Next's `_next/` asset dir (leading underscore) is included.
//
//go:embed all:webui/dist
var webuiDist embed.FS
// webuiHandler serves the embedded SPA. Public (no JWT) — auth is enforced
// client-side and on /api. Serves the exported per-route index.html files and
// falls back to index.html so client-side routing still resolves unknown paths.
func (s *Server) webuiHandler() http.Handler {
root, err := fs.Sub(webuiDist, "webui/dist")
if err != nil {
panic(err)
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
p := strings.TrimPrefix(path.Clean(r.URL.Path), "/")
if p == "" {
p = "index.html"
}
// 1) exact file (assets: _next/*, favicon.ico, ...)
// 2) route dir → <p>/index.html (trailingSlash export)
// 3) <p>.html
// 4) SPA 兜底 → index.html(交给客户端路由)
for _, cand := range []string{p, p + "/index.html", p + ".html"} {
if serveFileIfExists(w, r, root, cand) {
return
}
}
http.ServeFileFS(w, r, root, "index.html")
})
}
// serveFileIfExists serves name from fsys when it exists as a regular file.
func serveFileIfExists(w http.ResponseWriter, r *http.Request, fsys fs.FS, name string) bool {
f, err := fsys.Open(name)
if err != nil {
return false
}
st, statErr := f.Stat()
_ = f.Close()
if statErr != nil || st.IsDir() {
return false
}
http.ServeFileFS(w, r, fsys, name)
return true
}