优化:工具栏高度对齐+面板统一+远程连接架构+自动恢复预览
- 工具栏:面包屑与右侧组件像素级等高(:deep 34px)、合并重复search handler、统一分隔符样式、删除死代码 - 面板对齐:三面板header统一padding/font-size、文件列表分页固定底部(自定义紧凑)、表头默认隐藏、滚动条统一样式 - 预览区:始终显示空白预览面板、重启自动恢复上次打开文件 - 收藏夹:简化计数显示(共N项) - 远程连接:ConnectionIndicator自适应UI(无远程显示mini云图标)、ConnectionDialog支持编辑配置、transport抽象层(本地Wails/远程HTTP双模式)、agent后端模块
This commit is contained in:
64
internal/agent/handler/server_handler.go
Normal file
64
internal/agent/handler/server_handler.go
Normal file
@@ -0,0 +1,64 @@
|
||||
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("http://localhost:8073/localfs/html-preview?path=%s&theme=%s",
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user