优化:工具栏高度对齐+面板统一+远程连接架构+自动恢复预览
- 工具栏:面包屑与右侧组件像素级等高(:deep 34px)、合并重复search handler、统一分隔符样式、删除死代码 - 面板对齐:三面板header统一padding/font-size、文件列表分页固定底部(自定义紧凑)、表头默认隐藏、滚动条统一样式 - 预览区:始终显示空白预览面板、重启自动恢复上次打开文件 - 收藏夹:简化计数显示(共N项) - 远程连接:ConnectionIndicator自适应UI(无远程显示mini云图标)、ConnectionDialog支持编辑配置、transport抽象层(本地Wails/远程HTTP双模式)、agent后端模块
This commit is contained in:
@@ -68,9 +68,22 @@ func validateFilePath(rawPath string, logPrefix string) (string, error) {
|
||||
return "", ErrPathTraversal
|
||||
}
|
||||
|
||||
filePath := strings.ReplaceAll(decodedPath, "/", "\\")
|
||||
// 去除代理引入的 /localfs/ 前缀(可能有多层)
|
||||
clean := decodedPath
|
||||
for strings.HasPrefix(clean, "/localfs/") || strings.HasPrefix(clean, "localfs/") {
|
||||
clean = strings.TrimPrefix(clean, "/localfs/")
|
||||
clean = strings.TrimPrefix(clean, "localfs/")
|
||||
}
|
||||
|
||||
// 平台适配:Windows 用反斜杠,Linux/macOS 保持正斜杠
|
||||
filePath := filepath.FromSlash(clean)
|
||||
filePath = filepath.Clean(filePath)
|
||||
|
||||
// 确保绝对路径(Linux 以 / 开头,Windows 以盘符开头)
|
||||
if !filepath.IsAbs(filePath) && len(filePath) > 0 && filePath[0] != '/' && filePath[1] != ':' {
|
||||
filePath = "/" + filePath
|
||||
}
|
||||
|
||||
if !isSafePath(filePath) {
|
||||
return "", ErrPathUnsafe
|
||||
}
|
||||
@@ -153,8 +166,20 @@ func handleLocalFileRequest(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
log.Printf("[LocalFileHandler] 收到请求: %s", r.URL.Path)
|
||||
|
||||
// 从 URL 路径获取文件路径(移除 /localfs/ 前缀)
|
||||
pathPart := strings.TrimPrefix(r.URL.Path, "/localfs/")
|
||||
// 从 URL 路径获取文件路径(移除所有 /localfs/ 前缀,兼容代理双重前缀)
|
||||
pathPart := r.URL.Path
|
||||
for strings.HasPrefix(pathPart, "/localfs/") {
|
||||
pathPart = strings.TrimPrefix(pathPart, "/localfs/")
|
||||
}
|
||||
if pathPart == "" || pathPart == r.URL.Path {
|
||||
log.Printf("[LocalFileHandler] 路径前缀无效: original=%s", r.URL.Path)
|
||||
http.Error(w, "Invalid path", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
// 仅对非绝对路径添加前导 /(Windows 盘符路径如 D:/ 已经是绝对路径,不能加 /)
|
||||
if !strings.HasPrefix(pathPart, "/") && !regexp.MustCompile(`^[A-Za-z]:`).MatchString(pathPart) {
|
||||
pathPart = "/" + pathPart
|
||||
}
|
||||
|
||||
if pathPart == "" || pathPart == r.URL.Path {
|
||||
log.Printf("[LocalFileHandler] 路径前缀无效")
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
|
||||
19
internal/filesystem/file_lock_linux.go
Normal file
19
internal/filesystem/file_lock_linux.go
Normal file
@@ -0,0 +1,19 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package filesystem
|
||||
|
||||
// FileLockChecker 文件锁检查器(Linux 空实现)
|
||||
type FileLockChecker struct{}
|
||||
|
||||
func NewFileLockChecker() *FileLockChecker {
|
||||
return &FileLockChecker{}
|
||||
}
|
||||
|
||||
func (c *FileLockChecker) IsFileLocked(_path string) (bool, string, error) {
|
||||
return false, "", nil
|
||||
}
|
||||
|
||||
func (c *FileLockChecker) SafeDeleteWithLockCheck(_path string) error {
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user