Files
Agent-System/internal/controlcenter/webui/webui.go
T
weichen b0095de398 feat(control-center): P3 web SPA — overview, stage kanban, task pipeline, decisions, docs search
- control-center/web: Vite+React+TS SPA (hash routing, token login via
  localStorage, marked-lexer markdown rendering without raw HTML injection)
- pages: overview (stage x state matrix, SSE feed w/ 10s poll fallback),
  8-lane kanban + task create, task detail stepper/pipeline/ops
  (assign/advance/send-back/external-submit), decision inbox, docs browse
  + semantic/keyword/hybrid search, global stage settings, audit placeholder
- internal/controlcenter/webui: //go:embed dist, SPA fallback, index no-store;
  dist committed so go build works without node
- server: GET / serves embedded SPA; pipeline view now includes artifact
  content for console rendering
- archtest: allowlist controlcenter/server -> controlcenter/webui
2026-09-17 10:13:16 +00:00

53 lines
1.3 KiB
Go

// Package webui embeds the built Control Center SPA (control-center/web,
// output committed under dist/) and serves it with SPA fallback. The dist
// tree is committed so go build works without node.
package webui
import (
"embed"
"io/fs"
"net/http"
"path"
"strings"
)
//go:embed all:dist
var distFS embed.FS
// Handler serves static assets from dist; unknown paths fall back to
// index.html (client-side hash routing). index.html is always no-store so
// new deploys take effect immediately; hashed assets may be cached.
func Handler() http.Handler {
sub, err := fs.Sub(distFS, "dist")
if err != nil {
panic(err)
}
fileServer := http.FileServer(http.FS(sub))
index, err := fs.ReadFile(sub, "index.html")
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 == "" {
serveIndex(w, r, index)
return
}
if f, err := sub.Open(p); err == nil {
_ = f.Close()
fileServer.ServeHTTP(w, r)
return
}
serveIndex(w, r, index)
})
}
func serveIndex(w http.ResponseWriter, r *http.Request, index []byte) {
w.Header().Set("Cache-Control", "no-store")
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusOK)
if r.Method != http.MethodHead {
_, _ = w.Write(index)
}
}