- 工具栏:面包屑与右侧组件像素级等高(:deep 34px)、合并重复search handler、统一分隔符样式、删除死代码 - 面板对齐:三面板header统一padding/font-size、文件列表分页固定底部(自定义紧凑)、表头默认隐藏、滚动条统一样式 - 预览区:始终显示空白预览面板、重启自动恢复上次打开文件 - 收藏夹:简化计数显示(共N项) - 远程连接:ConnectionIndicator自适应UI(无远程显示mini云图标)、ConnectionDialog支持编辑配置、transport抽象层(本地Wails/远程HTTP双模式)、agent后端模块
177 lines
4.7 KiB
Go
177 lines
4.7 KiB
Go
package handler
|
|
|
|
import (
|
|
"net/http"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"u-desk/internal/agent/model"
|
|
"u-desk/internal/filesystem"
|
|
|
|
"github.com/labstack/echo/v4"
|
|
)
|
|
|
|
type writeFileReq struct {
|
|
Content string `json:"content"`
|
|
}
|
|
|
|
type createReq struct {
|
|
Type string `json:"type"` // "file" or "dir"
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
type renameReq struct {
|
|
NewPath string `json:"new_path"`
|
|
}
|
|
|
|
type uploadReq struct {
|
|
Content string `json:"content"` // base64 编码内容
|
|
}
|
|
|
|
// ListOrStat 列出目录或获取文件信息(?get=stat 时返回单文件信息)
|
|
func (h *Handler) ListOrStat(c echo.Context) error {
|
|
path := getPath(c)
|
|
action := c.QueryParam("get")
|
|
|
|
if action == "stat" {
|
|
info, err := h.fsSvc.GetFileInfo(path)
|
|
if err != nil {
|
|
return c.JSON(http.StatusOK, model.NotFound(err.Error()))
|
|
}
|
|
return c.JSON(http.StatusOK, model.OK(info))
|
|
}
|
|
|
|
files, err := h.fsSvc.ListDir(path)
|
|
if err != nil {
|
|
return c.JSON(http.StatusOK, model.InternalError(err.Error()))
|
|
}
|
|
// 限制返回数量,避免大目录导致前端卡顿
|
|
limit := c.QueryParam("limit")
|
|
if limit != "" {
|
|
n := 0
|
|
for i, f := range files {
|
|
if n >= 500 { // 硬限制 500 条
|
|
break
|
|
}
|
|
files[i] = f
|
|
n++
|
|
}
|
|
files = files[:n]
|
|
}
|
|
return c.JSON(http.StatusOK, model.OK(files))
|
|
}
|
|
|
|
// ReadFile 读取文件文本内容
|
|
func (h *Handler) ReadFile(c echo.Context) error {
|
|
path := getPath(c)
|
|
content, err := h.fsSvc.ReadFile(path)
|
|
if err != nil {
|
|
return c.JSON(http.StatusOK, model.InternalError(err.Error()))
|
|
}
|
|
return c.JSON(http.StatusOK, model.OK(map[string]string{
|
|
"content": content,
|
|
}))
|
|
}
|
|
|
|
// WriteFile 写入文件文本内容
|
|
func (h *Handler) WriteFile(c echo.Context) error {
|
|
path := getPath(c)
|
|
var req writeFileReq
|
|
if err := c.Bind(&req); err != nil {
|
|
return c.JSON(http.StatusBadRequest, model.BadRequest("无效请求体"))
|
|
}
|
|
if err := h.fsSvc.WriteFile(path, req.Content); err != nil {
|
|
return c.JSON(http.StatusInternalServerError, model.InternalError(err.Error()))
|
|
}
|
|
return c.JSON(http.StatusOK, model.NoContent())
|
|
}
|
|
|
|
// Create 创建文件或目录
|
|
func (h *Handler) Create(c echo.Context) error {
|
|
parentPath := getPath(c)
|
|
var req createReq
|
|
if err := c.Bind(&req); err != nil {
|
|
return c.JSON(http.StatusBadRequest, model.BadRequest("无效请求体"))
|
|
}
|
|
|
|
req.Name = strings.TrimSpace(req.Name)
|
|
if req.Name == "" {
|
|
return c.JSON(http.StatusBadRequest, model.BadRequest("名称不能为空"))
|
|
}
|
|
|
|
var result *filesystem.FileOperationResult
|
|
var err error
|
|
|
|
fullPath := filepath.Join(parentPath, req.Name)
|
|
|
|
switch req.Type {
|
|
case "dir":
|
|
result, err = h.fsSvc.CreateDir(fullPath)
|
|
default:
|
|
result, err = h.fsSvc.CreateFile(fullPath)
|
|
}
|
|
|
|
if err != nil {
|
|
return c.JSON(http.StatusInternalServerError, model.InternalError(err.Error()))
|
|
}
|
|
return c.JSON(http.StatusCreated, model.OK(result))
|
|
}
|
|
|
|
// Delete 删除文件或目录
|
|
func (h *Handler) Delete(c echo.Context) error {
|
|
path := getPath(c)
|
|
result, err := h.fsSvc.DeletePath(path)
|
|
if err != nil {
|
|
return c.JSON(http.StatusInternalServerError, model.InternalError(err.Error()))
|
|
}
|
|
return c.JSON(http.StatusOK, model.OK(result))
|
|
}
|
|
|
|
// Rename 重命名文件或目录
|
|
func (h *Handler) Rename(c echo.Context) error {
|
|
oldPath := getPath(c)
|
|
var req renameReq
|
|
if err := c.Bind(&req); err != nil {
|
|
return c.JSON(http.StatusBadRequest, model.BadRequest("无效请求体"))
|
|
}
|
|
req.NewPath = strings.TrimSpace(req.NewPath)
|
|
if req.NewPath == "" {
|
|
return c.JSON(http.StatusBadRequest, model.BadRequest("新路径不能为空"))
|
|
}
|
|
cleanNew := filepath.Clean(req.NewPath)
|
|
if strings.Contains(cleanNew, "..") {
|
|
return c.JSON(http.StatusBadRequest, model.BadRequest("新路径不允许包含 .."))
|
|
}
|
|
result, err := h.fsSvc.RenamePath(oldPath, cleanNew)
|
|
if err != nil {
|
|
return c.JSON(http.StatusInternalServerError, model.InternalError(err.Error()))
|
|
}
|
|
return c.JSON(http.StatusOK, model.OK(result))
|
|
}
|
|
|
|
// Upload 上传 Base64 编码的二进制文件
|
|
func (h *Handler) Upload(c echo.Context) error {
|
|
path := getPath(c)
|
|
var req uploadReq
|
|
if err := c.Bind(&req); err != nil {
|
|
return c.JSON(http.StatusBadRequest, model.BadRequest("无效请求体"))
|
|
}
|
|
if req.Content == "" {
|
|
return c.JSON(http.StatusBadRequest, model.BadRequest("内容不能为空"))
|
|
}
|
|
if err := h.fsSvc.SaveBase64File(path, req.Content); err != nil {
|
|
return c.JSON(http.StatusInternalServerError, model.InternalError(err.Error()))
|
|
}
|
|
return c.JSON(http.StatusOK, model.NoContent())
|
|
}
|
|
|
|
// DetectType 通过文件内容检测类型
|
|
func (h *Handler) DetectType(c echo.Context) error {
|
|
path := getPath(c)
|
|
info, err := h.fsSvc.DetectFileTypeByContent(path)
|
|
if err != nil {
|
|
return c.JSON(http.StatusOK, model.InternalError(err.Error()))
|
|
}
|
|
return c.JSON(http.StatusOK, model.OK(info))
|
|
}
|