diff --git a/pkg/filesystem/archive.go b/pkg/filesystem/archive.go index e988c70..843d425 100644 --- a/pkg/filesystem/archive.go +++ b/pkg/filesystem/archive.go @@ -33,6 +33,23 @@ func (fs *FileSystem) Compress(ctx context.Context, folderIDs, fileIDs []uint) ( return "", ErrDBListObjects } + // 如果上下文限制了父目录,则进行检查 + if parent, ok := ctx.Value(fsctx.LimitParentCtx).(*model.Folder); ok { + // 检查目录 + for _, folder := range folders { + if *folder.ParentID != parent.ID { + return "", ErrObjectNotExist + } + } + + // 检查文件 + for _, file := range files { + if file.FolderID != parent.ID { + return "", ErrObjectNotExist + } + } + } + // 尝试获取请求上下文,以便于后续检查用户取消任务 reqContext := ctx ginCtx, ok := ctx.Value(fsctx.GinCtx).(*gin.Context) diff --git a/pkg/filesystem/fsctx/context.go b/pkg/filesystem/fsctx/context.go index ff53258..c5700d0 100644 --- a/pkg/filesystem/fsctx/context.go +++ b/pkg/filesystem/fsctx/context.go @@ -29,4 +29,6 @@ const ( FileSizeCtx // ShareKeyCtx 分享文件的 HashID ShareKeyCtx + // LimitParentCtx 限制父目录 + LimitParentCtx ) diff --git a/routers/controllers/share.go b/routers/controllers/share.go index d74e727..754cae8 100644 --- a/routers/controllers/share.go +++ b/routers/controllers/share.go @@ -30,7 +30,7 @@ func GetShare(c *gin.Context) { // GetShareDownload 创建分享下载会话 func GetShareDownload(c *gin.Context) { - var service share.ShareService + var service share.Service if err := c.ShouldBindQuery(&service); err == nil { res := service.CreateDownloadSession(c) c.JSON(200, res) @@ -45,7 +45,7 @@ func PreviewShare(c *gin.Context) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - var service share.ShareService + var service share.Service if err := c.ShouldBindQuery(&service); err == nil { res := service.PreviewContent(ctx, c, false) // 是否需要重定向 @@ -68,7 +68,7 @@ func PreviewShareText(c *gin.Context) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - var service share.ShareService + var service share.Service if err := c.ShouldBindQuery(&service); err == nil { res := service.PreviewContent(ctx, c, true) // 是否有错误发生 @@ -82,7 +82,7 @@ func PreviewShareText(c *gin.Context) { // GetShareDocPreview 创建分享Office文档预览地址 func GetShareDocPreview(c *gin.Context) { - var service share.ShareService + var service share.Service if err := c.ShouldBindQuery(&service); err == nil { res := service.CreateDocPreviewSession(c) c.JSON(200, res) @@ -93,7 +93,7 @@ func GetShareDocPreview(c *gin.Context) { // SaveShare 转存他人分享 func SaveShare(c *gin.Context) { - var service share.ShareService + var service share.Service if err := c.ShouldBindJSON(&service); err == nil { res := service.SaveToMyFile(c) c.JSON(200, res) @@ -104,7 +104,7 @@ func SaveShare(c *gin.Context) { // ListSharedFolder 列出分享的目录下的对象 func ListSharedFolder(c *gin.Context) { - var service share.ShareService + var service share.Service if err := c.ShouldBindUri(&service); err == nil { res := service.List(c) c.JSON(200, res) @@ -112,3 +112,14 @@ func ListSharedFolder(c *gin.Context) { c.JSON(200, ErrorResponse(err)) } } + +// ArchiveShare 打包要下载的分享 +func ArchiveShare(c *gin.Context) { + var service share.ArchiveService + if err := c.ShouldBindJSON(&service); err == nil { + res := service.Archive(c) + c.JSON(200, res) + } else { + c.JSON(200, ErrorResponse(err)) + } +} diff --git a/routers/router.go b/routers/router.go index 5a019d4..952aa69 100644 --- a/routers/router.go +++ b/routers/router.go @@ -203,6 +203,12 @@ func InitMasterRouter() *gin.Engine { middleware.CheckShareUnlocked(), controllers.ListSharedFolder, ) + // 归档打包下载 + share.POST("archive/:id", + middleware.CheckShareUnlocked(), + middleware.BeforeShareDownload(), + controllers.ArchiveShare, + ) } // 需要登录保护的 diff --git a/service/share/visit.go b/service/share/visit.go index 4e3509b..f6a9b31 100644 --- a/service/share/visit.go +++ b/service/share/visit.go @@ -19,12 +19,19 @@ type ShareGetService struct { Password string `form:"password" binding:"max=255"` } -// ShareService 对分享进行操作的服务, +// Service 对分享进行操作的服务, // path 为可选文件完整路径,在目录分享下有效 -type ShareService struct { +type Service struct { Path string `form:"path" uri:"path" binding:"max=65535"` } +// ArchiveService 分享归档下载服务 +type ArchiveService struct { + Path string `json:"path" binding:"required,max=65535"` + Items []uint `json:"items" binding:"exists"` + Dirs []uint `json:"dirs" binding:"exists"` +} + // Get 获取分享内容 func (service *ShareGetService) Get(c *gin.Context) serializer.Response { shareCtx, _ := c.Get("share") @@ -62,7 +69,7 @@ func (service *ShareGetService) Get(c *gin.Context) serializer.Response { } // CreateDownloadSession 创建下载会话 -func (service *ShareService) CreateDownloadSession(c *gin.Context) serializer.Response { +func (service *Service) CreateDownloadSession(c *gin.Context) serializer.Response { shareCtx, _ := c.Get("share") share := shareCtx.(*model.Share) userCtx, _ := c.Get("user") @@ -100,7 +107,7 @@ func (service *ShareService) CreateDownloadSession(c *gin.Context) serializer.Re // PreviewContent 预览文件,需要登录会话, isText - 是否为文本文件,文本文件会 // 强制经由服务端中转 -func (service *ShareService) PreviewContent(ctx context.Context, c *gin.Context, isText bool) serializer.Response { +func (service *Service) PreviewContent(ctx context.Context, c *gin.Context, isText bool) serializer.Response { shareCtx, _ := c.Get("share") share := shareCtx.(*model.Share) @@ -118,7 +125,7 @@ func (service *ShareService) PreviewContent(ctx context.Context, c *gin.Context, } // CreateDocPreviewSession 创建Office预览会话,返回预览地址 -func (service *ShareService) CreateDocPreviewSession(c *gin.Context) serializer.Response { +func (service *Service) CreateDocPreviewSession(c *gin.Context) serializer.Response { shareCtx, _ := c.Get("share") share := shareCtx.(*model.Share) @@ -137,7 +144,7 @@ func (service *ShareService) CreateDocPreviewSession(c *gin.Context) serializer. } // SaveToMyFile 将此分享转存到自己的网盘 -func (service *ShareService) SaveToMyFile(c *gin.Context) serializer.Response { +func (service *Service) SaveToMyFile(c *gin.Context) serializer.Response { shareCtx, _ := c.Get("share") share := shareCtx.(*model.Share) userCtx, _ := c.Get("user") @@ -170,7 +177,7 @@ func (service *ShareService) SaveToMyFile(c *gin.Context) serializer.Response { } // List 列出分享的目录下的对象 -func (service *ShareService) List(c *gin.Context) serializer.Response { +func (service *Service) List(c *gin.Context) serializer.Response { shareCtx, _ := c.Get("share") share := shareCtx.(*model.Share) @@ -211,3 +218,50 @@ func (service *ShareService) List(c *gin.Context) serializer.Response { Data: objects, } } + +// Archive 创建批量下载归档 +func (service *ArchiveService) Archive(c *gin.Context) serializer.Response { + shareCtx, _ := c.Get("share") + share := shareCtx.(*model.Share) + userCtx, _ := c.Get("user") + user := userCtx.(*model.User) + + // 是否有权限 + if !user.Group.OptionsSerialized.ArchiveDownloadEnabled { + return serializer.Err(serializer.CodeNoPermissionErr, "您的用户组无权进行此操作", nil) + } + + if !share.IsDir { + return serializer.ParamErr("此分享无法进行打包", nil) + } + + // 创建文件系统 + fs, err := filesystem.NewFileSystem(user) + if err != nil { + return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + } + defer fs.Recycle() + + // 重设根目录 + fs.Root = share.GetSource().(*model.Folder) + + // 找到要打包文件的父目录 + exist, parent := fs.IsPathExist(service.Path) + if !exist { + return serializer.Err(serializer.CodeNotFound, "路径不存在", nil) + } + + ctx := context.WithValue(context.Background(), fsctx.LimitParentCtx, parent) + + // 用于调下层service + tempUser := share.GetCreator() + tempUser.Group.OptionsSerialized.ArchiveDownloadEnabled = true + c.Set("user", tempUser) + + subService := explorer.ItemService{ + Items: service.Items, + Dirs: service.Dirs, + } + + return subService.Archive(ctx, c) +}