From 8ab0fe0e2fd0d2339f9958890f7d4430d7b4c252 Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Fri, 29 Apr 2022 20:03:52 +0800 Subject: [PATCH] feat: search file under current folder --- models/file.go | 9 +++++-- pkg/filesystem/file.go | 17 ++++++++++++- pkg/serializer/error.go | 2 ++ routers/controllers/file.go | 14 ++++++++--- routers/controllers/share.go | 17 +++++++++++++ routers/router.go | 5 ++++ service/explorer/search.go | 10 ++++++++ service/share/visit.go | 47 ++++++++++++++++++++++++++++++++++++ 8 files changed, 114 insertions(+), 7 deletions(-) diff --git a/models/file.go b/models/file.go index 656ce4c..c4beeb8 100644 --- a/models/file.go +++ b/models/file.go @@ -117,8 +117,8 @@ func GetFilesByIDsFromTX(tx *gorm.DB, ids []uint, uid uint) ([]File, error) { } // GetFilesByKeywords 根据关键字搜索文件, -// UID为0表示忽略用户,只根据文件ID检索 -func GetFilesByKeywords(uid uint, keywords ...interface{}) ([]File, error) { +// UID为0表示忽略用户,只根据文件ID检索. 如果 parents 非空, 则只限制在 parent 包含的目录下搜索 +func GetFilesByKeywords(uid uint, parents []uint, keywords ...interface{}) ([]File, error) { var ( files []File result = DB @@ -136,6 +136,11 @@ func GetFilesByKeywords(uid uint, keywords ...interface{}) ([]File, error) { if uid != 0 { result = result.Where("user_id = ?", uid) } + + if len(parents) > 0 { + result = result.Where("folder_id in (?)", parents) + } + result = result.Where("("+conditions+")", keywords...).Find(&files) return files, result.Error diff --git a/pkg/filesystem/file.go b/pkg/filesystem/file.go index 0aa6780..457e625 100644 --- a/pkg/filesystem/file.go +++ b/pkg/filesystem/file.go @@ -2,6 +2,7 @@ package filesystem import ( "context" + "fmt" "io" model "github.com/cloudreve/Cloudreve/v3/models" @@ -361,7 +362,21 @@ func (fs *FileSystem) resetPolicyToFirstFile(ctx context.Context) error { // Search 搜索文件 func (fs *FileSystem) Search(ctx context.Context, keywords ...interface{}) ([]serializer.Object, error) { - files, _ := model.GetFilesByKeywords(fs.User.ID, keywords...) + parents := make([]uint, 0) + + // 如果限定了根目录,则只在这个根目录下搜索。 + if fs.Root != nil { + allFolders, err := model.GetRecursiveChildFolder([]uint{fs.Root.ID}, fs.User.ID, true) + if err != nil { + return nil, fmt.Errorf("failed to list all folders: %w", err) + } + + for _, folder := range allFolders { + parents = append(parents, folder.ID) + } + } + + files, _ := model.GetFilesByKeywords(fs.User.ID, parents, keywords...) fs.SetTargetFile(&files) return fs.listObjects(ctx, "/", files, nil, nil), nil diff --git a/pkg/serializer/error.go b/pkg/serializer/error.go index 4441b5e..d1c5670 100644 --- a/pkg/serializer/error.go +++ b/pkg/serializer/error.go @@ -84,6 +84,8 @@ const ( CodeBatchSourceSize = 40014 // CodeBatchAria2Size 超出最大 Aria2 任务数量限制 CodeBatchAria2Size = 40012 + // CodeParentNotExist 父目录不存在 + CodeParentNotExist = 40013 // CodeDBError 数据库操作失败 CodeDBError = 50001 // CodeEncryptError 加密失败 diff --git a/routers/controllers/file.go b/routers/controllers/file.go index 8dca01e..c77d2b0 100644 --- a/routers/controllers/file.go +++ b/routers/controllers/file.go @@ -363,12 +363,18 @@ func GetUploadSession(c *gin.Context) { // SearchFile 搜索文件 func SearchFile(c *gin.Context) { var service explorer.ItemSearchService - if err := c.ShouldBindUri(&service); err == nil { - res := service.Search(c) - c.JSON(200, res) - } else { + if err := c.ShouldBindUri(&service); err != nil { + c.JSON(200, ErrorResponse(err)) + return + } + + if err := c.ShouldBindQuery(&service); err != nil { c.JSON(200, ErrorResponse(err)) + return } + + res := service.Search(c) + c.JSON(200, res) } // CreateFile 创建空白文件 diff --git a/routers/controllers/share.go b/routers/controllers/share.go index 88b476d..f8e7f49 100644 --- a/routers/controllers/share.go +++ b/routers/controllers/share.go @@ -184,6 +184,23 @@ func ListSharedFolder(c *gin.Context) { } } +// SearchSharedFolder 搜索分享的目录下的对象 +func SearchSharedFolder(c *gin.Context) { + var service share.SearchService + if err := c.ShouldBindUri(&service); err != nil { + c.JSON(200, ErrorResponse(err)) + return + } + + if err := c.ShouldBindQuery(&service); err != nil { + c.JSON(200, ErrorResponse(err)) + return + } + + res := service.Search(c) + c.JSON(200, res) +} + // ArchiveShare 打包要下载的分享 func ArchiveShare(c *gin.Context) { var service share.ArchiveService diff --git a/routers/router.go b/routers/router.go index e2553d9..0727fe6 100644 --- a/routers/router.go +++ b/routers/router.go @@ -335,6 +335,11 @@ func InitMasterRouter() *gin.Engine { middleware.CheckShareUnlocked(), controllers.ListSharedFolder, ) + // 分享目录搜索 + share.GET("search/:id/:type/:keywords", + middleware.CheckShareUnlocked(), + controllers.SearchSharedFolder, + ) // 归档打包下载 share.POST("archive/:id", middleware.CheckShareUnlocked(), diff --git a/service/explorer/search.go b/service/explorer/search.go index 6a078f8..6a2b92f 100644 --- a/service/explorer/search.go +++ b/service/explorer/search.go @@ -15,6 +15,7 @@ import ( type ItemSearchService struct { Type string `uri:"type" binding:"required"` Keywords string `uri:"keywords" binding:"required"` + Path string `form:"path"` } // Search 执行搜索 @@ -26,6 +27,15 @@ func (service *ItemSearchService) Search(c *gin.Context) serializer.Response { } defer fs.Recycle() + if service.Path != "" { + ok, parent := fs.IsPathExist(service.Path) + if !ok { + return serializer.Err(serializer.CodeParentNotExist, "Cannot find parent folder", nil) + } + + fs.Root = parent + } + switch service.Type { case "keywords": return service.SearchKeywords(c, fs, "%"+service.Keywords+"%") diff --git a/service/share/visit.go b/service/share/visit.go index ce84d1b..2e07238 100644 --- a/service/share/visit.go +++ b/service/share/visit.go @@ -366,3 +366,50 @@ func (service *ArchiveService) Archive(c *gin.Context) serializer.Response { return subService.Archive(ctx, c) } + +// SearchService 对分享的目录进行搜索 +type SearchService struct { + explorer.ItemSearchService +} + +// Search 执行搜索 +func (service *SearchService) Search(c *gin.Context) serializer.Response { + shareCtx, _ := c.Get("share") + share := shareCtx.(*model.Share) + + if !share.IsDir { + return serializer.ParamErr("此分享无法列目录", nil) + } + + if service.Path != "" && !path.IsAbs(service.Path) { + return serializer.ParamErr("路径无效", nil) + } + + // 创建文件系统 + fs, err := filesystem.NewFileSystem(share.Creator()) + if err != nil { + return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + } + defer fs.Recycle() + + // 上下文 + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // 重设根目录 + fs.Root = share.Source().(*model.Folder) + fs.Root.Name = "/" + if service.Path != "" { + ok, parent := fs.IsPathExist(service.Path) + if !ok { + return serializer.Err(serializer.CodeParentNotExist, "Cannot find parent folder", nil) + } + + fs.Root = parent + } + + // 分享Key上下文 + ctx = context.WithValue(ctx, fsctx.ShareKeyCtx, hashid.HashID(share.ID, hashid.ShareID)) + + return service.SearchKeywords(c, fs, "%"+service.Keywords+"%") +}