package server import ( "fmt" "github.com/gin-gonic/gin" "os" "path/filepath" "strings" ) func validateWebUIDir(dir string) error { info, e := os.Stat(filepath.Join(dir, "index.html")) if e != nil || info.IsDir() { return fmt.Errorf("WEBUI_DIR must contain index.html") } return nil } func within(root, path string) bool { rel, e := filepath.Rel(root, path) return e == nil && rel != ".." && !strings.HasPrefix(rel, ".."+string(os.PathSeparator)) } func (s *Server) staticFallback(c *gin.Context) { if c.Request.URL.Path == "/api" || strings.HasPrefix(c.Request.URL.Path, "/api/") || s.WebUIDir == "" || c.Request.Method != "GET" && c.Request.Method != "HEAD" { reply(c, 404, "接口不存在", nil) return } root, e := filepath.EvalSymlinks(s.WebUIDir) if e != nil { reply(c, 404, "页面不存在", nil) return } root, e = filepath.Abs(root) if e != nil { reply(c, 404, "页面不存在", nil) return } candidate := filepath.Join(root, filepath.FromSlash(strings.TrimPrefix(c.Request.URL.Path, "/"))) if !within(root, candidate) { reply(c, 404, "页面不存在", nil) return } if resolved, e := filepath.EvalSymlinks(candidate); e == nil { if !within(root, resolved) { reply(c, 404, "页面不存在", nil) return } if info, e := os.Stat(resolved); e == nil && !info.IsDir() { c.File(resolved) return } } index, e := filepath.EvalSymlinks(filepath.Join(root, "index.html")) if e != nil || !within(root, index) { reply(c, 404, "页面不存在", nil) return } c.Header("Cache-Control", "no-cache") c.File(index) }