Files
core/internal/api/ui_design_assets.go

135 lines
3.3 KiB
Go

package api
import (
"errors"
"fmt"
"log"
"net/http"
"os"
"path/filepath"
"runtime"
"strings"
)
type uiDesignAssets struct {
RootPath string
CSSPath string
JSPath string
}
func registerUIDesignRoutes(mux *http.ServeMux) {
assets, err := resolveUIDesignAssets()
if err != nil {
log.Printf("ui design assets: %v", err)
return
}
log.Printf("ui design assets: using %s", assets.RootPath)
mux.HandleFunc("/ui-design/static/css/theme.css", func(w http.ResponseWriter, r *http.Request) {
serveUIDesignFile(w, r, "/ui-design/static/css/theme.css", assets.CSSPath, "text/css; charset=utf-8")
})
if assets.JSPath != "" {
mux.HandleFunc("/ui-design/static/js/app.js", func(w http.ResponseWriter, r *http.Request) {
serveUIDesignFile(w, r, "/ui-design/static/js/app.js", assets.JSPath, "application/javascript; charset=utf-8")
})
}
}
func serveUIDesignFile(w http.ResponseWriter, r *http.Request, routePath, filePath, contentType string) {
if r.URL.Path != routePath {
http.NotFound(w, r)
return
}
if r.Method != http.MethodGet && r.Method != http.MethodHead {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
if _, err := os.Stat(filePath); err != nil {
http.NotFound(w, r)
return
}
w.Header().Set("Cache-Control", "no-store")
w.Header().Set("Content-Type", contentType)
http.ServeFile(w, r, filePath)
}
func resolveUIDesignAssets() (uiDesignAssets, error) {
roots := candidateUIDesignRoots()
if len(roots) == 0 {
return uiDesignAssets{}, errors.New("no ui-design-code path candidates")
}
cssSuffixes := []string{
filepath.Join("demo", "web", "static", "css", "app.css"),
filepath.Join("kit", "patterns", "theme-vapor", "static", "vapor.css"),
filepath.Join("kit", "patterns", "theme-aqua-legacy", "demo-aqua-freeze.css"),
}
jsSuffixes := []string{
filepath.Join("demo", "web", "static", "js", "app.js"),
}
for _, root := range roots {
cssPath := firstExistingFile(root, cssSuffixes)
if cssPath == "" {
continue
}
jsPath := firstExistingFile(root, jsSuffixes)
return uiDesignAssets{
RootPath: root,
CSSPath: cssPath,
JSPath: jsPath,
}, nil
}
return uiDesignAssets{}, fmt.Errorf("ui-design-code not found in candidates: %s", strings.Join(roots, ", "))
}
func candidateUIDesignRoots() []string {
seen := make(map[string]struct{})
var roots []string
push := func(path string) {
if strings.TrimSpace(path) == "" {
return
}
abs, err := filepath.Abs(path)
if err != nil {
return
}
abs = filepath.Clean(abs)
if _, ok := seen[abs]; ok {
return
}
info, err := os.Stat(abs)
if err != nil || !info.IsDir() {
return
}
seen[abs] = struct{}{}
roots = append(roots, abs)
}
push(os.Getenv("UI_DESIGN_CODE_DIR"))
push(filepath.Join("tools", "ui-design-code"))
push(filepath.Join("..", "ui-design-code"))
if _, file, _, ok := runtime.Caller(0); ok {
apiDir := filepath.Dir(file)
coreRoot := filepath.Clean(filepath.Join(apiDir, "..", ".."))
push(filepath.Join(coreRoot, "tools", "ui-design-code"))
push(filepath.Join(coreRoot, "..", "ui-design-code"))
}
return roots
}
func firstExistingFile(root string, suffixes []string) string {
for _, suffix := range suffixes {
path := filepath.Join(root, suffix)
info, err := os.Stat(path)
if err == nil && !info.IsDir() {
return path
}
}
return ""
}