From 1038bae238172ea1d5e0181b52fa2f0313f6b5d9 Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Fri, 29 Apr 2022 19:59:25 +0800 Subject: [PATCH 01/10] feat: get file source link in batch --- assets | 2 +- models/group.go | 19 +++++++++++-------- models/migration.go | 6 +++++- pkg/serializer/error.go | 2 ++ pkg/serializer/explorer.go | 8 ++++++++ pkg/serializer/user.go | 2 ++ routers/controllers/file.go | 32 ++++++-------------------------- routers/router.go | 2 +- service/explorer/file.go | 37 +++++++++++++++++++++++++++++++++++++ 9 files changed, 73 insertions(+), 37 deletions(-) diff --git a/assets b/assets index c4a7dfea..e4ccca3e 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit c4a7dfea2c05077e6e7c7042c8598886bf6f5531 +Subproject commit e4ccca3e28f2df52646385ef3957e79f4d203f94 diff --git a/models/group.go b/models/group.go index b31f7cde..cc34d708 100644 --- a/models/group.go +++ b/models/group.go @@ -23,14 +23,17 @@ type Group struct { // GroupOption 用户组其他配置 type GroupOption struct { - ArchiveDownload bool `json:"archive_download,omitempty"` // 打包下载 - ArchiveTask bool `json:"archive_task,omitempty"` // 在线压缩 - CompressSize uint64 `json:"compress_size,omitempty"` // 可压缩大小 - DecompressSize uint64 `json:"decompress_size,omitempty"` - OneTimeDownload bool `json:"one_time_download,omitempty"` - ShareDownload bool `json:"share_download,omitempty"` - Aria2 bool `json:"aria2,omitempty"` // 离线下载 - Aria2Options map[string]interface{} `json:"aria2_options,omitempty"` // 离线下载用户组配置 + ArchiveDownload bool `json:"archive_download,omitempty"` // 打包下载 + ArchiveTask bool `json:"archive_task,omitempty"` // 在线压缩 + CompressSize uint64 `json:"compress_size,omitempty"` // 可压缩大小 + DecompressSize uint64 `json:"decompress_size,omitempty"` + OneTimeDownload bool `json:"one_time_download,omitempty"` + ShareDownload bool `json:"share_download,omitempty"` + Aria2 bool `json:"aria2,omitempty"` // 离线下载 + Aria2Options map[string]interface{} `json:"aria2_options,omitempty"` // 离线下载用户组配置 + SourceBatchSize int `json:"source_batch,omitempty"` + Aria2BatchSize int `json:"aria2_batch,omitempty"` + Aria2TaskSizeLimit uint64 `json:"aria2_size,omitempty"` } // GetGroupByID 用ID获取用户组 diff --git a/models/migration.go b/models/migration.go index ac36d322..07012b6a 100644 --- a/models/migration.go +++ b/models/migration.go @@ -108,6 +108,8 @@ func addDefaultGroups() { ArchiveTask: true, ShareDownload: true, Aria2: true, + SourceBatchSize: 1000, + Aria2BatchSize: 50, }, } if err := DB.Create(&defaultAdminGroup).Error; err != nil { @@ -126,7 +128,9 @@ func addDefaultGroups() { ShareEnabled: true, WebDAVEnabled: true, OptionsSerialized: GroupOption{ - ShareDownload: true, + ShareDownload: true, + SourceBatchSize: 10, + Aria2BatchSize: 1, }, } if err := DB.Create(&defaultAdminGroup).Error; err != nil { diff --git a/pkg/serializer/error.go b/pkg/serializer/error.go index 910f3f85..8de5d591 100644 --- a/pkg/serializer/error.go +++ b/pkg/serializer/error.go @@ -80,6 +80,8 @@ const ( CodeInvalidChunkIndex = 400012 // CodeInvalidContentLength 无效的正文长度 CodeInvalidContentLength = 400013 + // CodeBatchSourceSize 超出批量获取外链限制 + CodeBatchSourceSize = 40014 // CodeDBError 数据库操作失败 CodeDBError = 50001 // CodeEncryptError 加密失败 diff --git a/pkg/serializer/explorer.go b/pkg/serializer/explorer.go index 62fbeb77..afa5ac46 100644 --- a/pkg/serializer/explorer.go +++ b/pkg/serializer/explorer.go @@ -76,3 +76,11 @@ func BuildObjectList(parent uint, objects []Object, policy *model.Policy) Object return res } + +// Sources 获取外链的结果响应 +type Sources struct { + URL string `json:"url"` + Name string `json:"name"` + Parent uint `json:"parent"` + Error string `json:"error,omitempty"` +} diff --git a/pkg/serializer/user.go b/pkg/serializer/user.go index fcbc4db0..2df80bd1 100644 --- a/pkg/serializer/user.go +++ b/pkg/serializer/user.go @@ -40,6 +40,7 @@ type group struct { ShareDownload bool `json:"shareDownload"` CompressEnabled bool `json:"compress"` WebDAVEnabled bool `json:"webdav"` + SourceBatchSize int `json:"sourceBatch"` } type tag struct { @@ -98,6 +99,7 @@ func BuildUser(user model.User) User { ShareDownload: user.Group.OptionsSerialized.ShareDownload, CompressEnabled: user.Group.OptionsSerialized.ArchiveTask, WebDAVEnabled: user.Group.WebDAVEnabled, + SourceBatchSize: user.Group.OptionsSerialized.SourceBatchSize, }, Tags: buildTagRes(tags), } diff --git a/routers/controllers/file.go b/routers/controllers/file.go index 67bbb732..8dca01ec 100644 --- a/routers/controllers/file.go +++ b/routers/controllers/file.go @@ -121,20 +121,6 @@ func GetSource(c *gin.Context) { c.JSON(200, serializer.ParamErr("文件不存在", err)) return } - - sourceURL, err := fs.GetSource(ctx, fileID.(uint)) - if err != nil { - c.JSON(200, serializer.Err(serializer.CodeNotSet, err.Error(), err)) - return - } - - c.JSON(200, serializer.Response{ - Code: 0, - Data: struct { - URL string `json:"url"` - }{URL: sourceURL}, - }) - } // Thumb 获取文件缩略图 @@ -143,18 +129,12 @@ func Thumb(c *gin.Context) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - fs, err := filesystem.NewFileSystemFromContext(c) - if err != nil { - c.JSON(200, serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err)) - return - } - defer fs.Recycle() - - // 获取文件ID - fileID, ok := c.Get("object_id") - if !ok { - c.JSON(200, serializer.ParamErr("文件不存在", err)) - return + var service explorer.ItemIDService + if err := c.ShouldBindJSON(&service); err == nil { + res := service.Sources(ctx, c) + c.JSON(200, res) + } else { + c.JSON(200, ErrorResponse(err)) } // 获取缩略图 diff --git a/routers/router.go b/routers/router.go index da2cc4ce..e2553d9b 100644 --- a/routers/router.go +++ b/routers/router.go @@ -558,7 +558,7 @@ func InitMasterRouter() *gin.Engine { // 获取缩略图 file.GET("thumb/:id", controllers.Thumb) // 取得文件外链 - file.GET("source/:id", controllers.GetSource) + file.POST("source", controllers.GetSource) // 打包要下载的文件 file.POST("archive", controllers.Archive) // 创建文件压缩任务 diff --git a/service/explorer/file.go b/service/explorer/file.go index b58185bb..15c88b2e 100644 --- a/service/explorer/file.go +++ b/service/explorer/file.go @@ -428,3 +428,40 @@ func (service *FileIDService) PutContent(ctx context.Context, c *gin.Context) se Code: 0, } } + +// Sources 批量获取对象的外链 +func (s *ItemIDService) Sources(ctx context.Context, c *gin.Context) serializer.Response { + fs, err := filesystem.NewFileSystemFromContext(c) + if err != nil { + return serializer.Err(serializer.CodePolicyNotAllowed, "无法初始化文件系统", err) + } + defer fs.Recycle() + + if len(s.Raw().Items) > fs.User.Group.OptionsSerialized.SourceBatchSize { + return serializer.Err(serializer.CodeBatchSourceSize, "超出批量获取外链的最大数量限制", err) + } + + res := make([]serializer.Sources, 0, len(s.Raw().Items)) + for _, id := range s.Raw().Items { + fs.FileTarget = []model.File{} + sourceURL, err := fs.GetSource(ctx, id) + if len(fs.FileTarget) > 0 { + current := serializer.Sources{ + URL: sourceURL, + Name: fs.FileTarget[0].Name, + Parent: fs.FileTarget[0].FolderID, + } + + if err != nil { + current.Error = err.Error() + } + + res = append(res, current) + } + } + + return serializer.Response{ + Code: 0, + Data: res, + } +} From 94507fe609ef2c6d3b1a76c6bb950e1138178cdd Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Fri, 29 Apr 2022 20:01:43 +0800 Subject: [PATCH 02/10] feat: create aria2 task in batch --- models/group.go | 21 ++++++------ pkg/serializer/error.go | 2 ++ routers/controllers/aria2.go | 4 +-- service/aria2/add.go | 64 +++++++++++++++++++++++++++++++++--- 4 files changed, 73 insertions(+), 18 deletions(-) diff --git a/models/group.go b/models/group.go index cc34d708..78f7bfd1 100644 --- a/models/group.go +++ b/models/group.go @@ -23,17 +23,16 @@ type Group struct { // GroupOption 用户组其他配置 type GroupOption struct { - ArchiveDownload bool `json:"archive_download,omitempty"` // 打包下载 - ArchiveTask bool `json:"archive_task,omitempty"` // 在线压缩 - CompressSize uint64 `json:"compress_size,omitempty"` // 可压缩大小 - DecompressSize uint64 `json:"decompress_size,omitempty"` - OneTimeDownload bool `json:"one_time_download,omitempty"` - ShareDownload bool `json:"share_download,omitempty"` - Aria2 bool `json:"aria2,omitempty"` // 离线下载 - Aria2Options map[string]interface{} `json:"aria2_options,omitempty"` // 离线下载用户组配置 - SourceBatchSize int `json:"source_batch,omitempty"` - Aria2BatchSize int `json:"aria2_batch,omitempty"` - Aria2TaskSizeLimit uint64 `json:"aria2_size,omitempty"` + ArchiveDownload bool `json:"archive_download,omitempty"` // 打包下载 + ArchiveTask bool `json:"archive_task,omitempty"` // 在线压缩 + CompressSize uint64 `json:"compress_size,omitempty"` // 可压缩大小 + DecompressSize uint64 `json:"decompress_size,omitempty"` + OneTimeDownload bool `json:"one_time_download,omitempty"` + ShareDownload bool `json:"share_download,omitempty"` + Aria2 bool `json:"aria2,omitempty"` // 离线下载 + Aria2Options map[string]interface{} `json:"aria2_options,omitempty"` // 离线下载用户组配置 + SourceBatchSize int `json:"source_batch,omitempty"` + Aria2BatchSize int `json:"aria2_batch,omitempty"` } // GetGroupByID 用ID获取用户组 diff --git a/pkg/serializer/error.go b/pkg/serializer/error.go index 8de5d591..4441b5e7 100644 --- a/pkg/serializer/error.go +++ b/pkg/serializer/error.go @@ -82,6 +82,8 @@ const ( CodeInvalidContentLength = 400013 // CodeBatchSourceSize 超出批量获取外链限制 CodeBatchSourceSize = 40014 + // CodeBatchAria2Size 超出最大 Aria2 任务数量限制 + CodeBatchAria2Size = 40012 // CodeDBError 数据库操作失败 CodeDBError = 50001 // CodeEncryptError 加密失败 diff --git a/routers/controllers/aria2.go b/routers/controllers/aria2.go index 25a8fb09..38871795 100644 --- a/routers/controllers/aria2.go +++ b/routers/controllers/aria2.go @@ -11,7 +11,7 @@ import ( // AddAria2URL 添加离线下载URL func AddAria2URL(c *gin.Context) { - var addService aria2.AddURLService + var addService aria2.BatchAddURLService if err := c.ShouldBindJSON(&addService); err == nil { res := addService.Add(c, common.URLTask) c.JSON(200, res) @@ -52,7 +52,7 @@ func AddAria2Torrent(c *gin.Context) { if err := c.ShouldBindJSON(&addService); err == nil { addService.URL = res.Data.(string) - res := addService.Add(c, common.URLTask) + res := addService.Add(c, nil, common.URLTask) c.JSON(200, res) } else { c.JSON(200, ErrorResponse(err)) diff --git a/service/aria2/add.go b/service/aria2/add.go index 2c72c8b7..6b86069e 100644 --- a/service/aria2/add.go +++ b/service/aria2/add.go @@ -14,13 +14,13 @@ import ( ) // AddURLService 添加URL离线下载服务 -type AddURLService struct { - URL string `json:"url" binding:"required"` - Dst string `json:"dst" binding:"required,min=1"` +type BatchAddURLService struct { + URLs []string `json:"url" binding:"required"` + Dst string `json:"dst" binding:"required,min=1"` } -// Add 主机创建新的链接离线下载任务 -func (service *AddURLService) Add(c *gin.Context, taskType int) serializer.Response { +// Add 主机批量创建新的链接离线下载任务 +func (service *BatchAddURLService) Add(c *gin.Context, taskType int) serializer.Response { // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { @@ -38,6 +38,60 @@ func (service *AddURLService) Add(c *gin.Context, taskType int) serializer.Respo return serializer.Err(serializer.CodeNotFound, "存放路径不存在", nil) } + // 检查批量任务数量 + limit := fs.User.Group.OptionsSerialized.Aria2BatchSize + if limit > 0 && len(service.URLs) > limit { + return serializer.Err(serializer.CodeBatchAria2Size, "Exceed aria2 batch size", nil) + } + + res := make([]serializer.Response, 0, len(service.URLs)) + for _, target := range service.URLs { + subService := &AddURLService{ + URL: target, + Dst: service.Dst, + } + + addRes := subService.Add(c, fs, taskType) + res = append(res, addRes) + } + + return serializer.Response{Data: res} +} + +// AddURLService 添加URL离线下载服务 +type AddURLService struct { + URL string `json:"url" binding:"required"` + Dst string `json:"dst" binding:"required,min=1"` +} + +// Add 主机创建新的链接离线下载任务 +func (service *AddURLService) Add(c *gin.Context, fs *filesystem.FileSystem, taskType int) serializer.Response { + if fs == nil { + var err error + // 创建文件系统 + fs, err = filesystem.NewFileSystemFromContext(c) + if err != nil { + return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + } + defer fs.Recycle() + + // 检查用户组权限 + if !fs.User.Group.OptionsSerialized.Aria2 { + return serializer.Err(serializer.CodeGroupNotAllowed, "当前用户组无法进行此操作", nil) + } + + // 存放目录是否存在 + if exist, _ := fs.IsPathExist(service.Dst); !exist { + return serializer.Err(serializer.CodeNotFound, "存放路径不存在", nil) + } + } + + downloads := model.GetDownloadsByStatusAndUser(0, fs.User.ID, common.Downloading, common.Paused, common.Ready) + limit := fs.User.Group.OptionsSerialized.Aria2BatchSize + if limit > 0 && len(downloads)+1 > limit { + return serializer.Err(serializer.CodeBatchAria2Size, "Exceed aria2 batch size", nil) + } + // 创建任务 task := &model.Download{ Status: common.Ready, From 6af1eeb9fb7657140969133d62814d6537418041 Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Fri, 29 Apr 2022 20:02:29 +0800 Subject: [PATCH 03/10] fix: increase SharePoint size verify tolerance to 1 MB --- service/callback/upload.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/service/callback/upload.go b/service/callback/upload.go index fcb3f494..698d50c8 100644 --- a/service/callback/upload.go +++ b/service/callback/upload.go @@ -173,9 +173,9 @@ func (service *OneDriveCallback) PreProcess(c *gin.Context) serializer.Response actualPath := strings.TrimPrefix(uploadSession.SavePath, "/") isSizeCheckFailed := uploadSession.Size != info.Size - // SharePoint 会对 Office 文档增加 meta data 导致文件大小不一致,这里增加 100 KB 宽容 + // SharePoint 会对 Office 文档增加 meta data 导致文件大小不一致,这里增加 1 MB 宽容 // See: https://github.com/OneDrive/onedrive-api-docs/issues/935 - if strings.Contains(fs.Policy.OptionsSerialized.OdDriver, "sharepoint.com") && isSizeCheckFailed && (info.Size > uploadSession.Size) && (info.Size-uploadSession.Size <= 102400) { + if strings.Contains(fs.Policy.OptionsSerialized.OdDriver, "sharepoint.com") && isSizeCheckFailed && (info.Size > uploadSession.Size) && (info.Size-uploadSession.Size <= 1048576) { isSizeCheckFailed = false } From d51351eebdcfcdf8e4b7ac82359161a1e6a51373 Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Fri, 29 Apr 2022 20:02:55 +0800 Subject: [PATCH 04/10] fix: cannot generate thumbnail for COS policy --- pkg/filesystem/hooks.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/filesystem/hooks.go b/pkg/filesystem/hooks.go index 1f03301f..902dcce5 100644 --- a/pkg/filesystem/hooks.go +++ b/pkg/filesystem/hooks.go @@ -284,6 +284,10 @@ func HookPopPlaceholderToFile(picInfo string) Hook { return func(ctx context.Context, fs *FileSystem, fileHeader fsctx.FileHeader) error { fileInfo := fileHeader.Info() fileModel := fileInfo.Model.(*model.File) + if picInfo == "" && fs.Policy.IsThumbExist(fileInfo.FileName) { + picInfo = "1,1" + } + return fileModel.PopChunkToFile(fileInfo.LastModified, picInfo) } } 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 05/10] 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 656ce4ce..c4beeb83 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 0aa67800..457e6250 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 4441b5e7..d1c5670f 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 8dca01ec..c77d2b0e 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 88b476da..f8e7f49b 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 e2553d9b..0727fe6f 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 6a078f80..6a2b92fa 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 ce84d1bd..2e072380 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+"%") +} From 3b16d7d77cf6b9b3badee76345dcf04f8246590a Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Fri, 29 Apr 2022 20:04:26 +0800 Subject: [PATCH 06/10] fix: error code overlap --- pkg/serializer/error.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/serializer/error.go b/pkg/serializer/error.go index d1c5670f..3f7fd02c 100644 --- a/pkg/serializer/error.go +++ b/pkg/serializer/error.go @@ -83,9 +83,9 @@ const ( // CodeBatchSourceSize 超出批量获取外链限制 CodeBatchSourceSize = 40014 // CodeBatchAria2Size 超出最大 Aria2 任务数量限制 - CodeBatchAria2Size = 40012 + CodeBatchAria2Size = 40015 // CodeParentNotExist 父目录不存在 - CodeParentNotExist = 40013 + CodeParentNotExist = 40016 // CodeDBError 数据库操作失败 CodeDBError = 50001 // CodeEncryptError 加密失败 From a31ac2299a233219cbf04699454b97f910495a97 Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Fri, 29 Apr 2022 20:15:20 +0800 Subject: [PATCH 07/10] update version number --- assets | 2 +- pkg/conf/version.go | 4 ++-- routers/controllers/file.go | 33 ++++++++++++++++----------------- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/assets b/assets index e4ccca3e..1d11ce88 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit e4ccca3e28f2df52646385ef3957e79f4d203f94 +Subproject commit 1d11ce88ad85cceda1273da9ed8643f1ce3b6ee4 diff --git a/pkg/conf/version.go b/pkg/conf/version.go index 432d5920..d92c524d 100644 --- a/pkg/conf/version.go +++ b/pkg/conf/version.go @@ -1,13 +1,13 @@ package conf // BackendVersion 当前后端版本号 -var BackendVersion = "3.5.2" +var BackendVersion = "3.5.3" // RequiredDBVersion 与当前版本匹配的数据库版本 var RequiredDBVersion = "3.5.2" // RequiredStaticVersion 与当前版本匹配的静态资源版本 -var RequiredStaticVersion = "3.5.2" +var RequiredStaticVersion = "3.5.3" // IsPro 是否为Pro版本 var IsPro = "false" diff --git a/routers/controllers/file.go b/routers/controllers/file.go index c77d2b0e..fddc857a 100644 --- a/routers/controllers/file.go +++ b/routers/controllers/file.go @@ -3,10 +3,10 @@ package controllers import ( "context" "fmt" + "github.com/cloudreve/Cloudreve/v3/pkg/filesystem" "net/http" model "github.com/cloudreve/Cloudreve/v3/models" - "github.com/cloudreve/Cloudreve/v3/pkg/filesystem" "github.com/cloudreve/Cloudreve/v3/pkg/request" "github.com/cloudreve/Cloudreve/v3/pkg/serializer" "github.com/cloudreve/Cloudreve/v3/service/explorer" @@ -102,12 +102,26 @@ func AnonymousPermLink(c *gin.Context) { } } -// GetSource 获取文件的外链地址 func GetSource(c *gin.Context) { // 创建上下文 ctx, cancel := context.WithCancel(context.Background()) defer cancel() + var service explorer.ItemIDService + if err := c.ShouldBindJSON(&service); err == nil { + res := service.Sources(ctx, c) + c.JSON(200, res) + } else { + c.JSON(200, ErrorResponse(err)) + } +} + +// Thumb 获取文件缩略图 +func Thumb(c *gin.Context) { + // 创建上下文 + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { c.JSON(200, serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err)) @@ -121,21 +135,6 @@ func GetSource(c *gin.Context) { c.JSON(200, serializer.ParamErr("文件不存在", err)) return } -} - -// Thumb 获取文件缩略图 -func Thumb(c *gin.Context) { - // 创建上下文 - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - var service explorer.ItemIDService - if err := c.ShouldBindJSON(&service); err == nil { - res := service.Sources(ctx, c) - c.JSON(200, res) - } else { - c.JSON(200, ErrorResponse(err)) - } // 获取缩略图 resp, err := fs.GetThumb(ctx, fileID.(uint)) From 0e5683bc3b102a474b03992eb7932ac12643bcd1 Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Sat, 30 Apr 2022 10:02:57 +0800 Subject: [PATCH 08/10] test: search file with limited parent ids --- assets | 2 +- models/file.go | 2 +- models/file_test.go | 13 +++++++++++-- models/policy.go | 1 + pkg/filesystem/hooks_test.go | 19 +++++++++++++++++++ 5 files changed, 33 insertions(+), 4 deletions(-) diff --git a/assets b/assets index 1d11ce88..2d208929 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 1d11ce88ad85cceda1273da9ed8643f1ce3b6ee4 +Subproject commit 2d20892994d041953cecaba1ecc2d8be7abd5a4a diff --git a/models/file.go b/models/file.go index c4beeb83..34dad786 100644 --- a/models/file.go +++ b/models/file.go @@ -148,7 +148,7 @@ func GetFilesByKeywords(uid uint, parents []uint, keywords ...interface{}) ([]Fi // GetChildFilesOfFolders 批量检索目录子文件 func GetChildFilesOfFolders(folders *[]Folder) ([]File, error) { - // 将所有待删除目录ID抽离,以便检索文件 + // 将所有待检索目录ID抽离,以便检索文件 folderIDs := make([]uint, 0, len(*folders)) for _, value := range *folders { folderIDs = append(folderIDs, value.ID) diff --git a/models/file_test.go b/models/file_test.go index 68bf89c9..b421763a 100644 --- a/models/file_test.go +++ b/models/file_test.go @@ -561,7 +561,7 @@ func TestGetFilesByKeywords(t *testing.T) { // 未指定用户 { mock.ExpectQuery("SELECT(.+)").WithArgs("k1", "k2").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1)) - res, err := GetFilesByKeywords(0, "k1", "k2") + res, err := GetFilesByKeywords(0, nil, "k1", "k2") asserts.NoError(mock.ExpectationsWereMet()) asserts.NoError(err) asserts.Len(res, 1) @@ -570,7 +570,16 @@ func TestGetFilesByKeywords(t *testing.T) { // 指定用户 { mock.ExpectQuery("SELECT(.+)").WithArgs(1, "k1", "k2").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1)) - res, err := GetFilesByKeywords(1, "k1", "k2") + res, err := GetFilesByKeywords(1, nil, "k1", "k2") + asserts.NoError(mock.ExpectationsWereMet()) + asserts.NoError(err) + asserts.Len(res, 1) + } + + // 指定父目录 + { + mock.ExpectQuery("SELECT(.+)").WithArgs(1, 12, "k1", "k2").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1)) + res, err := GetFilesByKeywords(1, []uint{12}, "k1", "k2") asserts.NoError(mock.ExpectationsWereMet()) asserts.NoError(err) asserts.Len(res, 1) diff --git a/models/policy.go b/models/policy.go index a5f48268..1338a661 100644 --- a/models/policy.go +++ b/models/policy.go @@ -63,6 +63,7 @@ type PolicyOption struct { PlaceholderWithSize bool `json:"placeholder_with_size,omitempty"` } +// thumbSuffix 支持缩略图处理的文件扩展名 var thumbSuffix = map[string][]string{ "local": {}, "qiniu": {".psd", ".jpg", ".jpeg", ".png", ".gif", ".webp", ".tiff", ".bmp"}, diff --git a/pkg/filesystem/hooks_test.go b/pkg/filesystem/hooks_test.go index bf82d4c5..b8f2d635 100644 --- a/pkg/filesystem/hooks_test.go +++ b/pkg/filesystem/hooks_test.go @@ -671,6 +671,25 @@ func TestHookPopPlaceholderToFile(t *testing.T) { a.NoError(mock.ExpectationsWereMet()) } +func TestHookPopPlaceholderToFileBySuffix(t *testing.T) { + a := assert.New(t) + fs := &FileSystem{ + Policy: &model.Policy{Type: "cos"}, + } + file := &fsctx.FileStream{ + Name: "1.png", + Model: &model.File{ + Model: gorm.Model{ID: 1}, + }, + } + + mock.ExpectBegin() + mock.ExpectExec("UPDATE(.+)files(.+)").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectCommit() + a.NoError(HookPopPlaceholderToFile("")(context.Background(), fs, file)) + a.NoError(mock.ExpectationsWereMet()) +} + func TestHookDeleteUploadSession(t *testing.T) { a := assert.New(t) fs := &FileSystem{} From 4fe79859a9d5a977c50ed3aaeb57ebca1e360133 Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Sat, 30 Apr 2022 16:50:59 +0800 Subject: [PATCH 09/10] enhance: generate error message for parameter error i18n: use explicit error code for login controlelr --- pkg/serializer/error.go | 16 ++++++++++++++-- routers/controllers/main.go | 31 +++++++++++++++++-------------- service/user/login.go | 14 +++++++------- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/pkg/serializer/error.go b/pkg/serializer/error.go index 3f7fd02c..546af1be 100644 --- a/pkg/serializer/error.go +++ b/pkg/serializer/error.go @@ -58,8 +58,6 @@ const ( CodeConflict = 409 // CodeUploadFailed 上传出错 CodeUploadFailed = 40002 - // CodeCredentialInvalid 凭证无效 - CodeCredentialInvalid = 40001 // CodeCreateFolderFailed 目录创建失败 CodeCreateFolderFailed = 40003 // CodeObjectExist 对象已存在 @@ -86,6 +84,20 @@ const ( CodeBatchAria2Size = 40015 // CodeParentNotExist 父目录不存在 CodeParentNotExist = 40016 + // CodeUserBaned 用户不活跃 + CodeUserBaned = 40017 + // CodeUserNotActivated 用户不活跃 + CodeUserNotActivated = 40018 + // CodeFeatureNotEnabled 此功能未开启 + CodeFeatureNotEnabled = 40019 + // CodeCredentialInvalid 凭证无效 + CodeCredentialInvalid = 40020 + // CodeUserNotFound 用户不存在 + CodeUserNotFound = 40021 + // Code2FACodeErr 二步验证代码错误 + Code2FACodeErr = 40022 + // CodeLoginSessionNotExist 登录会话不存在 + CodeLoginSessionNotExist = 40023 // CodeDBError 数据库操作失败 CodeDBError = 50001 // CodeEncryptError 加密失败 diff --git a/routers/controllers/main.go b/routers/controllers/main.go index 18647700..3c94d293 100644 --- a/routers/controllers/main.go +++ b/routers/controllers/main.go @@ -6,30 +6,33 @@ import ( model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/serializer" "github.com/gin-gonic/gin" - "gopkg.in/go-playground/validator.v9" + "github.com/go-playground/validator/v10" ) // ParamErrorMsg 根据Validator返回的错误信息给出错误提示 func ParamErrorMsg(filed string, tag string) string { // 未通过验证的表单域与中文对应 fieldMap := map[string]string{ - "UserName": "邮箱", - "Password": "密码", - "Path": "路径", - "SourceID": "原始资源", - "URL": "链接", - "Nick": "昵称", + "UserName": "Email", + "Password": "Password", + "Path": "Path", + "SourceID": "Source resource", + "URL": "URL", + "Nick": "Nickname", } // 未通过的规则与中文对应 tagMap := map[string]string{ - "required": "不能为空", - "min": "太短", - "max": "太长", - "email": "格式不正确", + "required": "cannot be empty", + "min": "too short", + "max": "too long", + "email": "format error", } fieldVal, findField := fieldMap[filed] + if !findField { + fieldVal = filed + } tagVal, findTag := tagMap[tag] - if findField && findTag { + if findTag { // 返回拼接出来的错误信息 return fieldVal + tagVal } @@ -49,10 +52,10 @@ func ErrorResponse(err error) serializer.Response { } if _, ok := err.(*json.UnmarshalTypeError); ok { - return serializer.ParamErr("JSON类型不匹配", err) + return serializer.ParamErr("JSON marshall error", err) } - return serializer.ParamErr("参数错误", err) + return serializer.ParamErr("Parameter error", err) } // CurrentUser 获取当前用户 diff --git a/service/user/login.go b/service/user/login.go index 0e5c9218..3e7fa346 100644 --- a/service/user/login.go +++ b/service/user/login.go @@ -101,12 +101,12 @@ func (service *Enable2FA) Login(c *gin.Context) serializer.Response { // 查找用户 expectedUser, err := model.GetActiveUserByID(uid) if err != nil { - return serializer.Err(serializer.CodeNotFound, "用户不存在", nil) + return serializer.Err(serializer.CodeUserNotFound, "User not found", nil) } // 验证二步验证代码 if !totp.Validate(service.Code, expectedUser.TwoFactor) { - return serializer.ParamErr("验证代码不正确", nil) + return serializer.Err(serializer.Code2FACodeErr, "2FA code not correct", nil) } //登陆成功,清空并设置session @@ -118,7 +118,7 @@ func (service *Enable2FA) Login(c *gin.Context) serializer.Response { return serializer.BuildUserResponse(expectedUser) } - return serializer.Err(serializer.CodeNotFound, "登录会话不存在", nil) + return serializer.Err(serializer.CodeLoginSessionNotExist, "Login session not exist", nil) } // Login 用户登录函数 @@ -126,16 +126,16 @@ func (service *UserLoginService) Login(c *gin.Context) serializer.Response { expectedUser, err := model.GetUserByEmail(service.UserName) // 一系列校验 if err != nil { - return serializer.Err(serializer.CodeCredentialInvalid, "用户邮箱或密码错误", err) + return serializer.Err(serializer.CodeCredentialInvalid, "Wrong password or email address", err) } if authOK, _ := expectedUser.CheckPassword(service.Password); !authOK { - return serializer.Err(serializer.CodeCredentialInvalid, "用户邮箱或密码错误", nil) + return serializer.Err(serializer.CodeCredentialInvalid, "Wrong password or email address", nil) } if expectedUser.Status == model.Baned || expectedUser.Status == model.OveruseBaned { - return serializer.Err(403, "该账号已被封禁", nil) + return serializer.Err(serializer.CodeUserBaned, "This account has been blocked", nil) } if expectedUser.Status == model.NotActivicated { - return serializer.Err(403, "该账号未激活", nil) + return serializer.Err(serializer.CodeUserNotActivated, "This account is not activated", nil) } if expectedUser.TwoFactor != "" { From c9eefcb9462eaa70c57430233ac06835d9b98bcd Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Sat, 30 Apr 2022 16:51:24 +0800 Subject: [PATCH 10/10] i18n: captcha, reset password --- assets | 2 +- middleware/captcha.go | 21 +++++++++++++-------- middleware/common.go | 2 +- pkg/serializer/error.go | 14 ++++++++++++++ routers/controllers/main.go | 2 +- routers/controllers/user.go | 10 +++++----- service/user/login.go | 14 +++++++------- 7 files changed, 42 insertions(+), 23 deletions(-) diff --git a/assets b/assets index 2d208929..47493d67 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 2d20892994d041953cecaba1ecc2d8be7abd5a4a +Subproject commit 47493d67d7b08192a406f68cf2e9a31a46f37104 diff --git a/middleware/captcha.go b/middleware/captcha.go index cb1f845b..baf24a50 100644 --- a/middleware/captcha.go +++ b/middleware/captcha.go @@ -24,6 +24,11 @@ type req struct { Randstr string `json:"randstr"` } +const ( + captchaNotMatch = "CAPTCHA not match." + captchaRefresh = "Verification failed, please refresh the page and retry." +) + // CaptchaRequired 验证请求签名 func CaptchaRequired(configName string) gin.HandlerFunc { return func(c *gin.Context) { @@ -43,7 +48,7 @@ func CaptchaRequired(configName string) gin.HandlerFunc { bodyCopy := new(bytes.Buffer) _, err := io.Copy(bodyCopy, c.Request.Body) if err != nil { - c.JSON(200, serializer.ParamErr("验证码错误", err)) + c.JSON(200, serializer.Err(serializer.CodeCaptchaError, captchaNotMatch, err)) c.Abort() return } @@ -51,7 +56,7 @@ func CaptchaRequired(configName string) gin.HandlerFunc { bodyData := bodyCopy.Bytes() err = json.Unmarshal(bodyData, &service) if err != nil { - c.JSON(200, serializer.ParamErr("验证码错误", err)) + c.JSON(200, serializer.Err(serializer.CodeCaptchaError, captchaNotMatch, err)) c.Abort() return } @@ -62,7 +67,7 @@ func CaptchaRequired(configName string) gin.HandlerFunc { captchaID := util.GetSession(c, "captchaID") util.DeleteSession(c, "captchaID") if captchaID == nil || !base64Captcha.VerifyCaptcha(captchaID.(string), service.CaptchaCode) { - c.JSON(200, serializer.ParamErr("验证码错误", nil)) + c.JSON(200, serializer.Err(serializer.CodeCaptchaError, captchaNotMatch, err)) c.Abort() return } @@ -71,15 +76,15 @@ func CaptchaRequired(configName string) gin.HandlerFunc { case "recaptcha": reCAPTCHA, err := recaptcha.NewReCAPTCHA(options["captcha_ReCaptchaSecret"], recaptcha.V2, 10*time.Second) if err != nil { - util.Log().Warning("reCAPTCHA 验证错误, %s", err) + util.Log().Warning("reCAPTCHA verification failed, %s", err) c.Abort() break } err = reCAPTCHA.Verify(service.CaptchaCode) if err != nil { - util.Log().Warning("reCAPTCHA 验证错误, %s", err) - c.JSON(200, serializer.ParamErr("验证失败,请刷新网页后再次验证", nil)) + util.Log().Warning("reCAPTCHA verification failed, %s", err) + c.JSON(200, serializer.Err(serializer.CodeCaptchaRefreshNeeded, captchaRefresh, nil)) c.Abort() return } @@ -103,13 +108,13 @@ func CaptchaRequired(configName string) gin.HandlerFunc { request.UserIp = common.StringPtr(c.ClientIP()) response, err := client.DescribeCaptchaResult(request) if err != nil { - util.Log().Warning("TCaptcha 验证错误, %s", err) + util.Log().Warning("TCaptcha verification failed, %s", err) c.Abort() break } if *response.Response.CaptchaCode != int64(1) { - c.JSON(200, serializer.ParamErr("验证失败,请刷新网页后再次验证", nil)) + c.JSON(200, serializer.Err(serializer.CodeCaptchaRefreshNeeded, captchaRefresh, nil)) c.Abort() return } diff --git a/middleware/common.go b/middleware/common.go index c3a22837..8acc2c3c 100644 --- a/middleware/common.go +++ b/middleware/common.go @@ -30,7 +30,7 @@ func HashID(IDType int) gin.HandlerFunc { func IsFunctionEnabled(key string) gin.HandlerFunc { return func(c *gin.Context) { if !model.IsTrueVal(model.GetSettingByName(key)) { - c.JSON(200, serializer.Err(serializer.CodeNoPermissionErr, "未开启此功能", nil)) + c.JSON(200, serializer.Err(serializer.CodeFeatureNotEnabled, "This feature is not enabled", nil)) c.Abort() return } diff --git a/pkg/serializer/error.go b/pkg/serializer/error.go index 546af1be..d2646122 100644 --- a/pkg/serializer/error.go +++ b/pkg/serializer/error.go @@ -98,6 +98,20 @@ const ( Code2FACodeErr = 40022 // CodeLoginSessionNotExist 登录会话不存在 CodeLoginSessionNotExist = 40023 + // CodeInitializeAuthn 无法初始化 WebAuthn + CodeInitializeAuthn = 40024 + // CodeWebAuthnCredentialError WebAuthn 凭证无效 + CodeWebAuthnCredentialError = 40025 + // CodeCaptchaError 验证码错误 + CodeCaptchaError = 40026 + // CodeCaptchaRefreshNeeded 验证码需要刷新 + CodeCaptchaRefreshNeeded = 40027 + // CodeFailedSendEmail 邮件发送失败 + CodeFailedSendEmail = 40028 + // CodeInvalidTempLink 临时链接无效 + CodeInvalidTempLink = 40029 + // CodeTempLinkExpired 临时链接过期 + CodeTempLinkExpired = 40030 // CodeDBError 数据库操作失败 CodeDBError = 50001 // CodeEncryptError 加密失败 diff --git a/routers/controllers/main.go b/routers/controllers/main.go index 3c94d293..aaac8b22 100644 --- a/routers/controllers/main.go +++ b/routers/controllers/main.go @@ -34,7 +34,7 @@ func ParamErrorMsg(filed string, tag string) string { tagVal, findTag := tagMap[tag] if findTag { // 返回拼接出来的错误信息 - return fieldVal + tagVal + return fieldVal + " " + tagVal } return "" } diff --git a/routers/controllers/user.go b/routers/controllers/user.go index 7a10980f..a99b6c8f 100644 --- a/routers/controllers/user.go +++ b/routers/controllers/user.go @@ -20,13 +20,13 @@ func StartLoginAuthn(c *gin.Context) { userName := c.Param("username") expectedUser, err := model.GetActiveUserByEmail(userName) if err != nil { - c.JSON(200, serializer.Err(serializer.CodeNotFound, "用户不存在", err)) + c.JSON(200, serializer.Err(serializer.CodeUserNotFound, "User not exist", err)) return } instance, err := authn.NewAuthnInstance() if err != nil { - c.JSON(200, serializer.Err(serializer.CodeInternalSetting, "无法初始化Authn", err)) + c.JSON(200, serializer.Err(serializer.CodeInitializeAuthn, "Cannot initialize authn", err)) return } @@ -54,7 +54,7 @@ func FinishLoginAuthn(c *gin.Context) { userName := c.Param("username") expectedUser, err := model.GetActiveUserByEmail(userName) if err != nil { - c.JSON(200, serializer.Err(serializer.CodeCredentialInvalid, "用户邮箱或密码错误", err)) + c.JSON(200, serializer.Err(serializer.CodeUserNotFound, "User not exist", err)) return } @@ -65,14 +65,14 @@ func FinishLoginAuthn(c *gin.Context) { instance, err := authn.NewAuthnInstance() if err != nil { - c.JSON(200, serializer.Err(serializer.CodeInternalSetting, "无法初始化Authn", err)) + c.JSON(200, serializer.Err(serializer.CodeInitializeAuthn, "Cannot initialize authn", err)) return } _, err = instance.FinishLogin(expectedUser, sessionData, c.Request) if err != nil { - c.JSON(200, serializer.Err(serializer.CodeCredentialInvalid, "登录验证失败", err)) + c.JSON(200, serializer.Err(serializer.CodeWebAuthnCredentialError, "Verification failed", err)) return } diff --git a/service/user/login.go b/service/user/login.go index 3e7fa346..9367ae9b 100644 --- a/service/user/login.go +++ b/service/user/login.go @@ -37,24 +37,24 @@ func (service *UserResetService) Reset(c *gin.Context) serializer.Response { // 取得原始用户ID uid, err := hashid.DecodeHashID(service.ID, hashid.UserID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "重设链接无效", err) + return serializer.Err(serializer.CodeInvalidTempLink, "Invalid link", err) } // 检查重设会话 resetSession, exist := cache.Get(fmt.Sprintf("user_reset_%d", uid)) if !exist || resetSession.(string) != service.Secret { - return serializer.Err(serializer.CodeNotFound, "链接已过期", err) + return serializer.Err(serializer.CodeTempLinkExpired, "Link is expired", err) } // 重设用户密码 user, err := model.GetActiveUserByID(uid) if err != nil { - return serializer.Err(serializer.CodeNotFound, "用户不存在", err) + return serializer.Err(serializer.CodeUserNotFound, "User not found", nil) } user.SetPassword(service.Password) if err := user.Update(map[string]interface{}{"password": user.Password}); err != nil { - return serializer.DBErr("无法重设密码", err) + return serializer.DBErr("Failed to reset password", err) } cache.Deletes([]string{fmt.Sprintf("%d", uid)}, "user_reset_") @@ -67,10 +67,10 @@ func (service *UserResetEmailService) Reset(c *gin.Context) serializer.Response if user, err := model.GetUserByEmail(service.UserName); err == nil { if user.Status == model.Baned || user.Status == model.OveruseBaned { - return serializer.Err(403, "该账号已被封禁", nil) + return serializer.Err(serializer.CodeUserBaned, "This user is banned", nil) } if user.Status == model.NotActivicated { - return serializer.Err(403, "该账号未激活", nil) + return serializer.Err(serializer.CodeUserNotActivated, "This user is not activated", nil) } // 创建密码重设会话 secret := util.RandStringRunes(32) @@ -87,7 +87,7 @@ func (service *UserResetEmailService) Reset(c *gin.Context) serializer.Response // 发送密码重设邮件 title, body := email.NewResetEmail(user.Nick, finalURL.String()) if err := email.Send(user.Email, title, body); err != nil { - return serializer.Err(serializer.CodeInternalSetting, "无法发送密码重设邮件", err) + return serializer.Err(serializer.CodeFailedSendEmail, "Failed to send email", err) } }