package api import ( "net/http" "os" "path/filepath" "strings" "github.com/gin-gonic/gin" ) // configureWebUI adds an optional SPA fallback to the existing GoAdmin Gin // engine. API and framework routes keep their normal handlers; the fallback is // enabled only for Windows delivery packages that set SENSE_WEB_ROOT. func configureWebUI(r *gin.Engine) { root := strings.TrimSpace(os.Getenv("SENSE_WEB_ROOT")) if root == "" { return } absRoot, err := filepath.Abs(root) if err != nil { return } index := filepath.Join(absRoot, "index.html") if info, statErr := os.Stat(index); statErr != nil || info.IsDir() { return } r.NoRoute(func(c *gin.Context) { if c.Request.Method != http.MethodGet && c.Request.Method != http.MethodHead { c.Status(http.StatusNotFound) return } if isBackendPath(c.Request.URL.Path) { c.Status(http.StatusNotFound) return } requested := filepath.Clean(filepath.FromSlash(strings.TrimPrefix(c.Request.URL.Path, "/"))) if requested == "." { requested = "" } candidate := filepath.Join(absRoot, requested) if withinRoot(absRoot, candidate) { if info, statErr := os.Stat(candidate); statErr == nil && !info.IsDir() { c.File(candidate) return } } if filepath.Ext(requested) != "" { c.Status(http.StatusNotFound) return } c.File(index) }) } func withinRoot(root, candidate string) bool { rel, err := filepath.Rel(root, candidate) return err == nil && rel != ".." && !strings.HasPrefix(rel, ".."+string(filepath.Separator)) } func isBackendPath(path string) bool { for _, prefix := range []string{"/api/", "/swagger/", "/static/", "/form-generator/"} { if strings.HasPrefix(path, prefix) { return true } } return false }