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)) }