package handler import ( "fmt" "io" "net/http" "net/url" "path/filepath" "strings" "github.com/labstack/echo/v4" ) // FileServerProxy 反向代理到内置文件服务器(用于媒体预览) func (h *Handler) FileServerProxy(c echo.Context) error { rawPath := c.Param("*") if rawPath == "" { return c.String(http.StatusBadRequest, "缺少文件路径") } clean := filepath.Clean(rawPath) if strings.Contains(clean, "..") { return c.String(http.StatusForbidden, "路径不允许包含 ..") } // 防止多重 /localfs/ 前缀(循环去除所有) targetPath := filepath.ToSlash(clean) for strings.HasPrefix(targetPath, "localfs/") || strings.HasPrefix(targetPath, "localfs\\") { targetPath = strings.TrimPrefix(targetPath, "localfs/") targetPath = strings.TrimPrefix(targetPath, "localfs\\") } c.Request().URL.Path = "/localfs/" + targetPath h.fileProxy.ServeHTTP(c.Response(), c.Request()) return nil } // HTMLPreviewProxy 代理 HTML 预览请求(直连内部服务器,避免 ReverseProxy 路径拼接问题) func (h *Handler) HTMLPreviewProxy(c echo.Context) error { rawPath := c.QueryParam("path") if rawPath == "" { return c.String(http.StatusBadRequest, "缺少 path 参数") } clean := filepath.Clean(rawPath) if strings.Contains(clean, "..") { return c.String(http.StatusForbidden, "路径不允许包含 ..") } theme := c.QueryParam("theme") targetURL := fmt.Sprintf("%s/localfs/html-preview?path=%s&theme=%s", h.cfg.FileServerAddr(), url.QueryEscape(clean), url.QueryEscape(theme)) resp, err := http.Get(targetURL) if err != nil { return c.String(http.StatusBadGateway, "内部服务器不可用") } defer resp.Body.Close() for k, v := range resp.Header { c.Response().Header()[k] = v } c.Response().WriteHeader(resp.StatusCode) io.Copy(c.Response(), resp.Body) return nil }