From c99b36f7886220e301364b017884bbdb862a1e27 Mon Sep 17 00:00:00 2001 From: AHdark Date: Tue, 12 Jul 2022 19:20:14 +0800 Subject: [PATCH 01/16] chore: better way to remove frontend map files (#1380) * Feat: better way to remove frontend map files * Feat: Docker use `GENERATE_SOURCEMAP` in the frontend build section to avoid generating map files. --- Dockerfile | 6 ++++-- build.sh | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3ed9f10..c657018 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,8 +9,10 @@ RUN git clone --recurse-submodules https://github.com/cloudreve/Cloudreve.git # build frontend WORKDIR /cloudreve_builder/Cloudreve/assets +ENV GENERATE_SOURCEMAP false + RUN yarn install --network-timeout 1000000 -RUN yarn run build && find . -name "*.map" -type f -delete +RUN yarn run build # build backend WORKDIR /cloudreve_builder/Cloudreve @@ -39,4 +41,4 @@ RUN chmod +x ./cloudreve && mkdir -p /data/aria2 && chmod -R 766 /data/aria2 EXPOSE 5212 VOLUME ["/cloudreve/uploads", "/cloudreve/avatar", "/data"] -ENTRYPOINT ["./cloudreve"] \ No newline at end of file +ENTRYPOINT ["./cloudreve"] diff --git a/build.sh b/build.sh index 7e50a6e..8acac1a 100755 --- a/build.sh +++ b/build.sh @@ -24,13 +24,13 @@ buildAssets() { rm -rf assets/build export CI=false + export GENERATE_SOURCEMAP=false cd $REPO/assets yarn install yarn run build cd build - find . -name "*.map" -type f -delete cd $REPO zip -r - assets/build >assets.zip } From 9869671633d25785be36cd0f1e1437c5c05d84cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E7=99=BD-=E7=99=BD?= Date: Tue, 12 Jul 2022 19:30:41 +0800 Subject: [PATCH 02/16] fix: incorrect progress count (#1379) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: incorrect progress count 文件中转 已完成文件计数 应在文件成功上传后+1 #1367 * fix failed ut Co-authored-by: HFO4 <912394456@qq.com> --- pkg/task/tranfer.go | 8 +++++--- pkg/task/transfer_test.go | 10 ---------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/pkg/task/tranfer.go b/pkg/task/tranfer.go index 596ae9e..5f9aa58 100644 --- a/pkg/task/tranfer.go +++ b/pkg/task/tranfer.go @@ -96,9 +96,8 @@ func (job *TransferTask) Do() { return } - for index, file := range job.TaskProps.Src { - job.TaskModel.SetProgress(index) - + successCount := 0 + for _, file := range job.TaskProps.Src { dst := path.Join(job.TaskProps.Dst, filepath.Base(file)) if job.TaskProps.TrimPath { // 保留原始目录 @@ -132,6 +131,9 @@ func (job *TransferTask) Do() { if err != nil { job.SetErrorMsg("文件转存失败", err) + } else { + successCount++ + job.TaskModel.SetProgress(successCount) } } diff --git a/pkg/task/transfer_test.go b/pkg/task/transfer_test.go index 94301f6..612a453 100644 --- a/pkg/task/transfer_test.go +++ b/pkg/task/transfer_test.go @@ -88,11 +88,6 @@ func TestTransferTask_Do(t *testing.T) { } task.TaskProps.Src = []string{"test/not_exist"} task.TaskProps.Parent = "test/not_exist" - // 更新进度 - mock.ExpectBegin() - mock.ExpectExec("UPDATE(.+)").WillReturnResult(sqlmock.NewResult(1, - 1)) - mock.ExpectCommit() // 更新错误 mock.ExpectBegin() mock.ExpectExec("UPDATE(.+)").WillReturnResult(sqlmock.NewResult(1, @@ -113,11 +108,6 @@ func TestTransferTask_Do(t *testing.T) { task.TaskProps.Src = []string{"test/not_exist"} task.TaskProps.Parent = "test/not_exist" task.TaskProps.TrimPath = true - // 更新进度 - mock.ExpectBegin() - mock.ExpectExec("UPDATE(.+)").WillReturnResult(sqlmock.NewResult(1, - 1)) - mock.ExpectCommit() // 更新错误 mock.ExpectBegin() mock.ExpectExec("UPDATE(.+)").WillReturnResult(sqlmock.NewResult(1, From a1880672b17841dc670b9af05520dd7276022f6c Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Mon, 18 Jul 2022 20:03:57 +0800 Subject: [PATCH 03/16] i18n: error codes for dashboard operations --- assets | 2 +- pkg/serializer/error.go | 28 +++++++++++++++++++++++++ service/admin/aria2.go | 10 ++++----- service/admin/file.go | 16 +++++++-------- service/admin/group.go | 15 +++++++------- service/admin/node.go | 18 ++++++++--------- service/admin/policy.go | 45 +++++++++++++++++++++-------------------- service/admin/share.go | 2 +- service/admin/site.go | 8 ++++---- service/admin/task.go | 6 +++--- service/admin/user.go | 18 ++++++++--------- 11 files changed, 98 insertions(+), 70 deletions(-) diff --git a/assets b/assets index 41f585a..f47af47 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 41f585a6f8c8f99ed4b2e279555d6b4dcdf957bc +Subproject commit f47af47efeecae015dd28a76ab797665391029e0 diff --git a/pkg/serializer/error.go b/pkg/serializer/error.go index bfac810..2b31aa2 100644 --- a/pkg/serializer/error.go +++ b/pkg/serializer/error.go @@ -118,6 +118,30 @@ const ( CodeEmailSent = 40033 // CodeUserCannotActivate 用户无法激活 CodeUserCannotActivate = 40034 + // 存储策略不存在 + CodePolicyNotExist = 40035 + // 无法删除默认存储策略 + CodeDeleteDefaultPolicy = 40036 + // 存储策略下还有文件 + CodePolicyUsedByFiles = 40037 + // 存储策略绑定了用户组 + CodePolicyUsedByGroups = 40038 + // 用户组不存在 + CodeGroupNotFound = 40039 + // 对系统用户组执行非法操作 + CodeInvalidActionOnSystemGroup = 40040 + // 用户组正在被使用 + CodeGroupUsedByUser = 40041 + // 为初始用户更改用户组 + CodeChangeGroupForDefaultUser = 40042 + // 对系统用户执行非法操作 + CodeInvalidActionOnDefaultUser = 40043 + // 文件不存在 + CodeFileNotFound = 40044 + // 列取文件失败 + CodeListFilesError = 40045 + // 对系统节点进行非法操作 + CodeInvalidActionOnSystemNode = 40046 // CodeDBError 数据库操作失败 CodeDBError = 50001 // CodeEncryptError 加密失败 @@ -130,6 +154,10 @@ const ( CodeCacheOperation = 50006 // CodeCallbackError 回调失败 CodeCallbackError = 50007 + // 后台设置更新失败 + CodeUpdateSetting = 50008 + // 跨域策略添加失败 + CodeAddCORS = 50009 //CodeParamErr 各种奇奇怪怪的参数错误 CodeParamErr = 40001 // CodeNotSet 未定错误,后续尝试从error中获取 diff --git a/service/admin/aria2.go b/service/admin/aria2.go index b40603b..6a2b77d 100644 --- a/service/admin/aria2.go +++ b/service/admin/aria2.go @@ -26,11 +26,11 @@ type Aria2TestService struct { func (service *Aria2TestService) TestMaster() serializer.Response { res, err := aria2.TestRPCConnection(service.RPC, service.Token, 5) if err != nil { - return serializer.ParamErr(err.Error(), err) + return serializer.ParamErr("Failed to connect to RPC server: "+err.Error(), err) } if res.Version == "" { - return serializer.ParamErr("RPC 服务返回非预期响应", nil) + return serializer.ParamErr("RPC server returns unexpected response", nil) } return serializer.Response{Data: res.Version} @@ -39,7 +39,7 @@ func (service *Aria2TestService) TestMaster() serializer.Response { func (service *Aria2TestService) TestSlave() serializer.Response { slave, err := url.Parse(service.Server) if err != nil { - return serializer.ParamErr("无法解析从机端地址,"+err.Error(), nil) + return serializer.ParamErr("Cannot parse slave server URL, "+err.Error(), nil) } controller, _ := url.Parse("/api/v3/slave/ping/aria2") @@ -60,11 +60,11 @@ func (service *Aria2TestService) TestSlave() serializer.Response { ), ).DecodeResponse() if err != nil { - return serializer.ParamErr("无连接到从机,"+err.Error(), nil) + return serializer.ParamErr("Failed to connect to slave node, "+err.Error(), nil) } if res.Code != 0 { - return serializer.ParamErr("成功接到从机,但是从机返回:"+res.Msg, nil) + return serializer.ParamErr("Successfully connected to slave, but slave returns: "+res.Msg, nil) } return serializer.Response{Data: res.Data.(string)} diff --git a/service/admin/file.go b/service/admin/file.go index eeb4222..8c6c6e9 100644 --- a/service/admin/file.go +++ b/service/admin/file.go @@ -36,13 +36,13 @@ func (service *ListFolderService) List(c *gin.Context) serializer.Response { // 列取存储策略中的目录 policy, err := model.GetPolicyByID(service.ID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "存储策略不存在", err) + return serializer.Err(serializer.CodePolicyNotExist, "", err) } // 创建文件系统 fs, err := filesystem.NewAnonymousFileSystem() if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "无法创建文件系统", err) + return serializer.Err(serializer.CodeInternalSetting, "Failed to create filesystem.", err) } defer fs.Recycle() @@ -50,7 +50,7 @@ func (service *ListFolderService) List(c *gin.Context) serializer.Response { fs.Policy = &policy res, err := fs.ListPhysical(c.Request.Context(), service.Path) if err != nil { - return serializer.Err(serializer.CodeIOFailed, "无法列取目录", err) + return serializer.Err(serializer.CodeListFilesError, "", err) } return serializer.Response{ @@ -63,20 +63,20 @@ func (service *ListFolderService) List(c *gin.Context) serializer.Response { // 查找用户 user, err := model.GetUserByID(service.ID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "用户不存在", err) + return serializer.Err(serializer.CodeUserNotFound, "用户不存在", err) } // 创建文件系统 fs, err := filesystem.NewFileSystem(&user) if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "无法创建文件系统", err) + return serializer.Err(serializer.CodeInternalSetting, "Failed to create filesystem.", err) } defer fs.Recycle() // 列取目录 res, err := fs.List(c.Request.Context(), service.Path, nil) if err != nil { - return serializer.Err(serializer.CodeIOFailed, "无法列取目录", err) + return serializer.Err(serializer.CodeListFilesError, "", err) } return serializer.Response{ @@ -88,7 +88,7 @@ func (service *ListFolderService) List(c *gin.Context) serializer.Response { func (service *FileBatchService) Delete(c *gin.Context) serializer.Response { files, err := model.GetFilesByIDs(service.ID, 0) if err != nil { - return serializer.DBErr("无法列出待删除文件", err) + return serializer.DBErr("Failed to list files for deleting", err) } // 根据用户分组 @@ -135,7 +135,7 @@ func (service *FileBatchService) Delete(c *gin.Context) serializer.Response { func (service *FileService) Get(c *gin.Context) serializer.Response { file, err := model.GetFilesByIDs([]uint{service.ID}, 0) if err != nil { - return serializer.Err(serializer.CodeNotFound, "文件不存在", err) + return serializer.Err(serializer.CodeFileNotFound, "", err) } ctx := context.WithValue(context.Background(), fsctx.FileModelCtx, &file[0]) diff --git a/service/admin/group.go b/service/admin/group.go index c761611..272ac99 100644 --- a/service/admin/group.go +++ b/service/admin/group.go @@ -1,10 +1,9 @@ package admin import ( - "fmt" - model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/serializer" + "strconv" ) // AddGroupService 用户组添加服务 @@ -21,7 +20,7 @@ type GroupService struct { func (service *GroupService) Get() serializer.Response { group, err := model.GetGroupByID(service.ID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "存储策略不存在", err) + return serializer.Err(serializer.CodeGroupNotFound, "", err) } return serializer.Response{Data: group} @@ -32,12 +31,12 @@ func (service *GroupService) Delete() serializer.Response { // 查找用户组 group, err := model.GetGroupByID(service.ID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "用户组不存在", err) + return serializer.Err(serializer.CodeGroupNotFound, "", err) } // 是否为系统用户组 if group.ID <= 3 { - return serializer.Err(serializer.CodeNoPermissionErr, "系统用户组无法删除", err) + return serializer.Err(serializer.CodeInvalidActionOnSystemGroup, "", err) } // 检查是否有用户使用 @@ -46,7 +45,7 @@ func (service *GroupService) Delete() serializer.Response { Select("count(id)").Row() row.Scan(&total) if total > 0 { - return serializer.ParamErr(fmt.Sprintf("有 %d 位用户仍属于此用户组,请先删除这些用户或者更改用户组", total), nil) + return serializer.Err(serializer.CodeGroupUsedByUser, strconv.Itoa(total), nil) } model.DB.Delete(&group) @@ -58,11 +57,11 @@ func (service *GroupService) Delete() serializer.Response { func (service *AddGroupService) Add() serializer.Response { if service.Group.ID > 0 { if err := model.DB.Save(&service.Group).Error; err != nil { - return serializer.ParamErr("用户组保存失败", err) + return serializer.DBErr("Failed to save group record", err) } } else { if err := model.DB.Create(&service.Group).Error; err != nil { - return serializer.ParamErr("用户组添加失败", err) + return serializer.DBErr("Failed to create group record", err) } } diff --git a/service/admin/node.go b/service/admin/node.go index e5294c6..c861058 100644 --- a/service/admin/node.go +++ b/service/admin/node.go @@ -16,11 +16,11 @@ type AddNodeService struct { func (service *AddNodeService) Add() serializer.Response { if service.Node.ID > 0 { if err := model.DB.Save(&service.Node).Error; err != nil { - return serializer.ParamErr("节点保存失败", err) + return serializer.DBErr("Failed to save node record", err) } } else { if err := model.DB.Create(&service.Node).Error; err != nil { - return serializer.ParamErr("节点添加失败", err) + return serializer.DBErr("Failed to create node record", err) } } @@ -84,16 +84,16 @@ type ToggleNodeService struct { func (service *ToggleNodeService) Toggle() serializer.Response { node, err := model.GetNodeByID(service.ID) if err != nil { - return serializer.DBErr("找不到节点", err) + return serializer.DBErr("Node not found", err) } // 是否为系统节点 if node.ID <= 1 { - return serializer.Err(serializer.CodeNoPermissionErr, "系统节点无法更改", err) + return serializer.Err(serializer.CodeInvalidActionOnSystemNode, "", err) } if err = node.SetStatus(service.Desired); err != nil { - return serializer.DBErr("无法更改节点状态", err) + return serializer.DBErr("Failed to change node status", err) } if service.Desired == model.NodeActive { @@ -115,17 +115,17 @@ func (service *NodeService) Delete() serializer.Response { // 查找用户组 node, err := model.GetNodeByID(service.ID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "节点不存在", err) + return serializer.DBErr("Node record not found", err) } // 是否为系统节点 if node.ID <= 1 { - return serializer.Err(serializer.CodeNoPermissionErr, "系统节点无法删除", err) + return serializer.Err(serializer.CodeInvalidActionOnSystemNode, "", err) } cluster.Default.Delete(node.ID) if err := model.DB.Delete(&node).Error; err != nil { - return serializer.DBErr("无法删除节点", err) + return serializer.DBErr("Failed to delete node record", err) } return serializer.Response{} @@ -135,7 +135,7 @@ func (service *NodeService) Delete() serializer.Response { func (service *NodeService) Get() serializer.Response { node, err := model.GetNodeByID(service.ID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "节点不存在", err) + return serializer.DBErr("Node not exist", err) } return serializer.Response{Data: node} diff --git a/service/admin/policy.go b/service/admin/policy.go index a9151d5..f9d40a8 100644 --- a/service/admin/policy.go +++ b/service/admin/policy.go @@ -9,6 +9,7 @@ import ( "net/url" "os" "path/filepath" + "strconv" "strings" "time" @@ -58,12 +59,12 @@ type PolicyService struct { func (service *PolicyService) Delete() serializer.Response { // 禁止删除默认策略 if service.ID == 1 { - return serializer.Err(serializer.CodeNoPermissionErr, "默认存储策略无法删除", nil) + return serializer.Err(serializer.CodeDeleteDefaultPolicy, "", nil) } policy, err := model.GetPolicyByID(service.ID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "存储策略不存在", err) + return serializer.Err(serializer.CodePolicyNotExist, "", err) } // 检查是否有文件使用 @@ -72,7 +73,7 @@ func (service *PolicyService) Delete() serializer.Response { Select("count(id)").Row() row.Scan(&total) if total > 0 { - return serializer.ParamErr(fmt.Sprintf("有 %d 个文件仍在使用此存储策略,请先删除这些文件", total), nil) + return serializer.Err(serializer.CodePolicyUsedByFiles, strconv.Itoa(total), nil) } // 检查用户组使用 @@ -83,7 +84,7 @@ func (service *PolicyService) Delete() serializer.Response { ).Find(&groups) if len(groups) > 0 { - return serializer.ParamErr(fmt.Sprintf("有 %d 个用户组绑定了此存储策略,请先解除绑定", len(groups)), nil) + return serializer.Err(serializer.CodePolicyUsedByGroups, strconv.Itoa(len(groups)), nil) } model.DB.Delete(&policy) @@ -96,7 +97,7 @@ func (service *PolicyService) Delete() serializer.Response { func (service *PolicyService) Get() serializer.Response { policy, err := model.GetPolicyByID(service.ID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "存储策略不存在", err) + return serializer.Err(serializer.CodePolicyNotExist, "", err) } return serializer.Response{Data: policy} @@ -106,12 +107,12 @@ func (service *PolicyService) Get() serializer.Response { func (service *PolicyService) GetOAuth(c *gin.Context) serializer.Response { policy, err := model.GetPolicyByID(service.ID) if err != nil || policy.Type != "onedrive" { - return serializer.Err(serializer.CodeNotFound, "存储策略不存在", nil) + return serializer.Err(serializer.CodePolicyNotExist, "", nil) } client, err := onedrive.NewClient(&policy) if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "无法初始化 OneDrive 客户端", err) + return serializer.Err(serializer.CodeInternalSetting, "Failed to initialize OneDrive client", err) } util.SetSession(c, map[string]interface{}{ @@ -130,11 +131,11 @@ func (service *PolicyService) GetOAuth(c *gin.Context) serializer.Response { func (service *PolicyService) AddSCF() serializer.Response { policy, err := model.GetPolicyByID(service.ID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "存储策略不存在", nil) + return serializer.Err(serializer.CodePolicyNotExist, "", nil) } if err := cos.CreateSCF(&policy, service.Region); err != nil { - return serializer.Err(serializer.CodeInternalSetting, "云函数创建失败", err) + return serializer.ParamErr("Failed to create SCF function", err) } return serializer.Response{} @@ -144,17 +145,17 @@ func (service *PolicyService) AddSCF() serializer.Response { func (service *PolicyService) AddCORS() serializer.Response { policy, err := model.GetPolicyByID(service.ID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "存储策略不存在", nil) + return serializer.Err(serializer.CodePolicyNotExist, "", nil) } switch policy.Type { case "oss": handler, err := oss.NewDriver(&policy) if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "跨域策略添加失败", err) + return serializer.Err(serializer.CodeAddCORS, "", err) } if err := handler.CORS(); err != nil { - return serializer.Err(serializer.CodeInternalSetting, "跨域策略添加失败", err) + return serializer.Err(serializer.CodeAddCORS, "", err) } case "cos": u, _ := url.Parse(policy.Server) @@ -171,19 +172,19 @@ func (service *PolicyService) AddCORS() serializer.Response { } if err := handler.CORS(); err != nil { - return serializer.Err(serializer.CodeInternalSetting, "跨域策略添加失败", err) + return serializer.Err(serializer.CodeAddCORS, "", err) } case "s3": handler, err := s3.NewDriver(&policy) if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "跨域策略添加失败", err) + return serializer.Err(serializer.CodeAddCORS, "", err) } if err := handler.CORS(); err != nil { - return serializer.Err(serializer.CodeInternalSetting, "跨域策略添加失败", err) + return serializer.Err(serializer.CodeAddCORS, "", err) } default: - return serializer.ParamErr("不支持此策略", nil) + return serializer.Err(serializer.CodePolicyNotAllowed, "", nil) } return serializer.Response{} @@ -225,7 +226,7 @@ func (service *SlavePingService) Test() serializer.Response { func (service *SlaveTestService) Test() serializer.Response { slave, err := url.Parse(service.Server) if err != nil { - return serializer.ParamErr("无法解析从机端地址,"+err.Error(), nil) + return serializer.ParamErr("Failed to parse slave node server URL: "+err.Error(), nil) } controller, _ := url.Parse("/api/v3/slave/ping") @@ -248,11 +249,11 @@ func (service *SlaveTestService) Test() serializer.Response { ), ).DecodeResponse() if err != nil { - return serializer.ParamErr("无连接到从机,"+err.Error(), nil) + return serializer.ParamErr("Failed to connect to slave node: "+err.Error(), nil) } if res.Code != 0 { - return serializer.ParamErr("成功接到从机,但是从机返回:"+res.Msg, nil) + return serializer.ParamErr("Successfully connected to slave node, but slave returns: "+res.Msg, nil) } return serializer.Response{} @@ -266,11 +267,11 @@ func (service *AddPolicyService) Add() serializer.Response { if service.Policy.ID > 0 { if err := model.DB.Save(&service.Policy).Error; err != nil { - return serializer.ParamErr("存储策略保存失败", err) + return serializer.DBErr("Failed to save policy", err) } } else { if err := model.DB.Create(&service.Policy).Error; err != nil { - return serializer.ParamErr("存储策略添加失败", err) + return serializer.DBErr("Failed to create policy", err) } } @@ -286,7 +287,7 @@ func (service *PathTestService) Test() serializer.Response { path = filepath.Join(path, "test.txt") file, err := util.CreatNestedFile(util.RelativePath(path)) if err != nil { - return serializer.ParamErr(fmt.Sprintf("无法创建路径 %s , %s", path, err.Error()), nil) + return serializer.ParamErr(fmt.Sprintf("Failed to create \"%s\": %s", path, err.Error()), nil) } file.Close() diff --git a/service/admin/share.go b/service/admin/share.go index 5846bb2..66d89fa 100644 --- a/service/admin/share.go +++ b/service/admin/share.go @@ -17,7 +17,7 @@ type ShareBatchService struct { // Delete 删除文件 func (service *ShareBatchService) Delete(c *gin.Context) serializer.Response { if err := model.DB.Where("id in (?)", service.ID).Delete(&model.Share{}).Error; err != nil { - return serializer.DBErr("无法删除分享", err) + return serializer.DBErr("Failed to delete share record", err) } return serializer.Response{} } diff --git a/service/admin/site.go b/service/admin/site.go index 12bee1f..918b33b 100644 --- a/service/admin/site.go +++ b/service/admin/site.go @@ -43,8 +43,8 @@ type MailTestService struct { // Send 发送测试邮件 func (service *MailTestService) Send() serializer.Response { - if err := email.Send(service.Email, "Cloudreve发信测试", "这是一封测试邮件,用于测试 Cloudreve 发信设置。"); err != nil { - return serializer.Err(serializer.CodeInternalSetting, "发信失败, "+err.Error(), nil) + if err := email.Send(service.Email, "Cloudreve Email delivery test", "This is a test Email, to test Cloudreve Email delivery settings"); err != nil { + return serializer.Err(serializer.CodeFailedSendEmail, err.Error(), nil) } return serializer.Response{} } @@ -65,14 +65,14 @@ func (service *BatchSettingChangeService) Change() serializer.Response { if err := tx.Model(&model.Setting{}).Where("name = ?", setting.Key).Update("value", setting.Value).Error; err != nil { cache.Deletes(cacheClean, "setting_") tx.Rollback() - return serializer.DBErr("设置 "+setting.Key+" 更新失败", err) + return serializer.Err(serializer.CodeUpdateSetting, "Setting "+setting.Key+" failed to update", err) } cacheClean = append(cacheClean, setting.Key) } if err := tx.Commit().Error; err != nil { - return serializer.DBErr("设置更新失败", err) + return serializer.DBErr("Failed to update setting", err) } cache.Deletes(cacheClean, "setting_") diff --git a/service/admin/task.go b/service/admin/task.go index 6b0f861..2146d46 100644 --- a/service/admin/task.go +++ b/service/admin/task.go @@ -28,7 +28,7 @@ func (service *ImportTaskService) Create(c *gin.Context, user *model.User) seria // 创建任务 job, err := task.NewImportTask(service.UID, service.PolicyID, service.Src, service.Dst, service.Recursive) if err != nil { - return serializer.Err(serializer.CodeNotSet, "任务创建失败", err) + return serializer.DBErr("Failed to create task record.", err) } task.TaskPoll.Submit(job) return serializer.Response{} @@ -37,7 +37,7 @@ func (service *ImportTaskService) Create(c *gin.Context, user *model.User) seria // Delete 删除任务 func (service *TaskBatchService) Delete(c *gin.Context) serializer.Response { if err := model.DB.Where("id in (?)", service.ID).Delete(&model.Download{}).Error; err != nil { - return serializer.DBErr("无法删除任务", err) + return serializer.DBErr("Failed to delete task records", err) } return serializer.Response{} } @@ -45,7 +45,7 @@ func (service *TaskBatchService) Delete(c *gin.Context) serializer.Response { // DeleteGeneral 删除常规任务 func (service *TaskBatchService) DeleteGeneral(c *gin.Context) serializer.Response { if err := model.DB.Where("id in (?)", service.ID).Delete(&model.Task{}).Error; err != nil { - return serializer.DBErr("无法删除任务", err) + return serializer.DBErr("Failed to delete task records", err) } return serializer.Response{} } diff --git a/service/admin/user.go b/service/admin/user.go index f4a58a9..32af8b3 100644 --- a/service/admin/user.go +++ b/service/admin/user.go @@ -29,11 +29,11 @@ type UserBatchService struct { func (service *UserService) Ban() serializer.Response { user, err := model.GetUserByID(service.ID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "用户不存在", err) + return serializer.Err(serializer.CodeUserNotFound, "", err) } if user.ID == 1 { - return serializer.Err(serializer.CodeNoPermissionErr, "无法封禁初始用户", err) + return serializer.Err(serializer.CodeInvalidActionOnDefaultUser, "", err) } if user.Status == model.Active { @@ -50,12 +50,12 @@ func (service *UserBatchService) Delete() serializer.Response { for _, uid := range service.ID { user, err := model.GetUserByID(uid) if err != nil { - return serializer.Err(serializer.CodeNotFound, "用户不存在", err) + return serializer.Err(serializer.CodeUserNotFound, "", err) } // 不能删除初始用户 if uid == 1 { - return serializer.Err(serializer.CodeNoPermissionErr, "无法删除初始用户", err) + return serializer.Err(serializer.CodeInvalidActionOnDefaultUser, "", err) } // 删除与此用户相关的所有资源 @@ -64,7 +64,7 @@ func (service *UserBatchService) Delete() serializer.Response { // 删除所有文件 root, err := fs.User.Root() if err != nil { - return serializer.Err(serializer.CodeNotFound, "无法找到用户根目录", err) + return serializer.Err(serializer.CodeInternalSetting, "User's root folder not exist", err) } fs.Delete(context.Background(), []uint{root.ID}, []uint{}, false) @@ -89,7 +89,7 @@ func (service *UserBatchService) Delete() serializer.Response { func (service *UserService) Get() serializer.Response { group, err := model.GetUserByID(service.ID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "用户不存在", err) + return serializer.Err(serializer.CodeUserNotFound, "", err) } return serializer.Response{Data: group} @@ -112,16 +112,16 @@ func (service *AddUserService) Add() serializer.Response { // 检查愚蠢操作 if user.ID == 1 && user.GroupID != 1 { - return serializer.ParamErr("无法更改初始用户的用户组", nil) + return serializer.Err(serializer.CodeChangeGroupForDefaultUser, "", nil) } if err := model.DB.Save(&user).Error; err != nil { - return serializer.ParamErr("用户保存失败", err) + return serializer.DBErr("Failed to save user record", err) } } else { service.User.SetPassword(service.Password) if err := model.DB.Create(&service.User).Error; err != nil { - return serializer.ParamErr("用户组添加失败", err) + return serializer.DBErr("Failed to create user record", err) } } From 08104646ba12715669051377876b004e64e29e25 Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Wed, 20 Jul 2022 19:59:13 +0800 Subject: [PATCH 04/16] i18n: error codes for aria2 / callback/ directory operation --- assets | 2 +- pkg/filesystem/errors.go | 31 +++++++++++++++---------------- pkg/serializer/error.go | 25 ++++++++++++++++++++++++- routers/controllers/callback.go | 2 +- service/admin/file.go | 4 ++-- service/aria2/add.go | 22 +++++++++++----------- service/aria2/manage.go | 14 +++++++------- service/callback/upload.go | 24 ++++++++++++------------ service/explorer/directory.go | 4 ++-- service/explorer/file.go | 2 +- 10 files changed, 76 insertions(+), 54 deletions(-) diff --git a/assets b/assets index f47af47..a1028e7 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit f47af47efeecae015dd28a76ab797665391029e0 +Subproject commit a1028e7e0ae96be4bb67d8c117cf39e07c207473 diff --git a/pkg/filesystem/errors.go b/pkg/filesystem/errors.go index 63d1961..6ca9e3a 100644 --- a/pkg/filesystem/errors.go +++ b/pkg/filesystem/errors.go @@ -7,20 +7,19 @@ import ( ) var ( - ErrUnknownPolicyType = errors.New("未知存储策略类型") - ErrFileSizeTooBig = errors.New("单个文件尺寸太大") - ErrFileExtensionNotAllowed = errors.New("不允许上传此类型的文件") - ErrInsufficientCapacity = errors.New("容量空间不足") - ErrIllegalObjectName = errors.New("目标名称非法") - ErrClientCanceled = errors.New("客户端取消操作") - ErrRootProtected = errors.New("无法对根目录进行操作") - ErrInsertFileRecord = serializer.NewError(serializer.CodeDBError, "无法插入文件记录", nil) - ErrFileExisted = serializer.NewError(serializer.CodeObjectExist, "同名文件或目录已存在", nil) - ErrFileUploadSessionExisted = serializer.NewError(serializer.CodeObjectExist, "当前目录下已经有同名文件正在上传中,请尝试清空上传会话", nil) - ErrFolderExisted = serializer.NewError(serializer.CodeObjectExist, "同名目录已存在", nil) - ErrPathNotExist = serializer.NewError(404, "路径不存在", nil) - ErrObjectNotExist = serializer.NewError(404, "文件不存在", nil) - ErrIO = serializer.NewError(serializer.CodeIOFailed, "无法读取文件数据", nil) - ErrDBListObjects = serializer.NewError(serializer.CodeDBError, "无法列取对象记录", nil) - ErrDBDeleteObjects = serializer.NewError(serializer.CodeDBError, "无法删除对象记录", nil) + ErrUnknownPolicyType = serializer.NewError(serializer.CodeInternalSetting, "Unknown policy type", nil) + ErrFileSizeTooBig = serializer.NewError(serializer.CodeFileTooLarge, "", nil) + ErrFileExtensionNotAllowed = serializer.NewError(serializer.CodeFileTypeNotAllowed, "", nil) + ErrInsufficientCapacity = serializer.NewError(serializer.CodeInsufficientCapacity, "", nil) + ErrIllegalObjectName = serializer.NewError(serializer.CodeIllegalObjectName, "", nil) + ErrClientCanceled = errors.New("Client canceled operation") + ErrRootProtected = serializer.NewError(serializer.CodeRootProtected, "", nil) + ErrInsertFileRecord = serializer.NewError(serializer.CodeDBError, "Failed to create file record", nil) + ErrFileExisted = serializer.NewError(serializer.CodeObjectExist, "", nil) + ErrFileUploadSessionExisted = serializer.NewError(serializer.CodeConflictUploadOngoing, "", nil) + ErrPathNotExist = serializer.NewError(serializer.CodeParentNotExist, "", nil) + ErrObjectNotExist = serializer.NewError(serializer.CodeParentNotExist, "", nil) + ErrIO = serializer.NewError(serializer.CodeIOFailed, "Failed to read file data", nil) + ErrDBListObjects = serializer.NewError(serializer.CodeDBError, "Failed to list object records", nil) + ErrDBDeleteObjects = serializer.NewError(serializer.CodeDBError, "Failed to delete object records", nil) ) diff --git a/pkg/serializer/error.go b/pkg/serializer/error.go index 2b31aa2..19f520c 100644 --- a/pkg/serializer/error.go +++ b/pkg/serializer/error.go @@ -142,6 +142,24 @@ const ( CodeListFilesError = 40045 // 对系统节点进行非法操作 CodeInvalidActionOnSystemNode = 40046 + // 创建文件系统出错 + CodeCreateFSError = 40047 + // 创建任务出错 + CodeCreateTaskError = 40048 + // 文件尺寸太大 + CodeFileTooLarge = 40049 + // 文件类型不允许 + CodeFileTypeNotAllowed = 40050 + // 用户容量不足 + CodeInsufficientCapacity = 40051 + // 对象名非法 + CodeIllegalObjectName = 40052 + // 不支持对根目录执行此操作 + CodeRootProtected = 40053 + // 当前目录下已经有同名文件正在上传中 + CodeConflictUploadOngoing = 40054 + // 文件信息不一致 + CodeMetaMismatch = 40055 // CodeDBError 数据库操作失败 CodeDBError = 50001 // CodeEncryptError 加密失败 @@ -158,6 +176,10 @@ const ( CodeUpdateSetting = 50008 // 跨域策略添加失败 CodeAddCORS = 50009 + // 节点不可用 + CodeNodeOffline = 50010 + // 文件元信息查询失败 + CodeQueryMetaFailed = 50011 //CodeParamErr 各种奇奇怪怪的参数错误 CodeParamErr = 40001 // CodeNotSet 未定错误,后续尝试从error中获取 @@ -183,7 +205,8 @@ func ParamErr(msg string, err error) Response { // Err 通用错误处理 func Err(errCode int, msg string, err error) Response { // 底层错误是AppError,则尝试从AppError中获取详细信息 - if appError, ok := err.(AppError); ok { + var appError AppError + if errors.As(err, &appError) { errCode = appError.Code err = appError.RawError msg = appError.Msg diff --git a/routers/controllers/callback.go b/routers/controllers/callback.go index dade566..ba3ab46 100644 --- a/routers/controllers/callback.go +++ b/routers/controllers/callback.go @@ -57,7 +57,7 @@ func UpyunCallback(c *gin.Context) { if err := c.ShouldBind(&callbackBody); err == nil { if callbackBody.Code != 200 { util.Log().Debug( - "又拍云回调返回错误代码%d,信息:%s", + "Upload callback returned error code:%d, message: %s", callbackBody.Code, callbackBody.Message, ) diff --git a/service/admin/file.go b/service/admin/file.go index 8c6c6e9..1d7bc2b 100644 --- a/service/admin/file.go +++ b/service/admin/file.go @@ -42,7 +42,7 @@ func (service *ListFolderService) List(c *gin.Context) serializer.Response { // 创建文件系统 fs, err := filesystem.NewAnonymousFileSystem() if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "Failed to create filesystem.", err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() @@ -69,7 +69,7 @@ func (service *ListFolderService) List(c *gin.Context) serializer.Response { // 创建文件系统 fs, err := filesystem.NewFileSystem(&user) if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "Failed to create filesystem.", err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() diff --git a/service/aria2/add.go b/service/aria2/add.go index 6b86069..52b5964 100644 --- a/service/aria2/add.go +++ b/service/aria2/add.go @@ -24,24 +24,24 @@ func (service *BatchAddURLService) Add(c *gin.Context, taskType int) serializer. // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() // 检查用户组权限 if !fs.User.Group.OptionsSerialized.Aria2 { - return serializer.Err(serializer.CodeGroupNotAllowed, "当前用户组无法进行此操作", nil) + return serializer.Err(serializer.CodeGroupNotAllowed, "", nil) } // 存放目录是否存在 if exist, _ := fs.IsPathExist(service.Dst); !exist { - return serializer.Err(serializer.CodeNotFound, "存放路径不存在", nil) + return serializer.Err(serializer.CodeParentNotExist, "", nil) } // 检查批量任务数量 limit := fs.User.Group.OptionsSerialized.Aria2BatchSize if limit > 0 && len(service.URLs) > limit { - return serializer.Err(serializer.CodeBatchAria2Size, "Exceed aria2 batch size", nil) + return serializer.Err(serializer.CodeBatchAria2Size, "", nil) } res := make([]serializer.Response, 0, len(service.URLs)) @@ -71,25 +71,25 @@ func (service *AddURLService) Add(c *gin.Context, fs *filesystem.FileSystem, tas // 创建文件系统 fs, err = filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() // 检查用户组权限 if !fs.User.Group.OptionsSerialized.Aria2 { - return serializer.Err(serializer.CodeGroupNotAllowed, "当前用户组无法进行此操作", nil) + return serializer.Err(serializer.CodeGroupNotAllowed, "", nil) } // 存放目录是否存在 if exist, _ := fs.IsPathExist(service.Dst); !exist { - return serializer.Err(serializer.CodeNotFound, "存放路径不存在", nil) + return serializer.Err(serializer.CodeParentNotExist, "", 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) + return serializer.Err(serializer.CodeBatchAria2Size, "", nil) } // 创建任务 @@ -107,20 +107,20 @@ func (service *AddURLService) Add(c *gin.Context, fs *filesystem.FileSystem, tas // 获取 Aria2 实例 err, node := cluster.Default.BalanceNodeByFeature("aria2", lb) if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "Aria2 实例获取失败", err) + return serializer.Err(serializer.CodeInternalSetting, "Failed to get Aria2 instance", err) } // 创建任务 gid, err := node.GetAria2Instance().CreateTask(task, fs.User.Group.OptionsSerialized.Aria2Options) if err != nil { - return serializer.Err(serializer.CodeNotSet, "任务创建失败", err) + return serializer.Err(serializer.CodeCreateTaskError, "", err) } task.GID = gid task.NodeID = node.ID() _, err = task.Create() if err != nil { - return serializer.DBErr("任务创建失败", err) + return serializer.DBErr("Failed to create task record", err) } // 创建任务监控 diff --git a/service/aria2/manage.go b/service/aria2/manage.go index 34e17b9..a8cd7f6 100644 --- a/service/aria2/manage.go +++ b/service/aria2/manage.go @@ -54,13 +54,13 @@ func (service *DownloadTaskService) Delete(c *gin.Context) serializer.Response { // 查找下载记录 download, err := model.GetDownloadByGid(c.Param("gid"), user.ID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "下载记录不存在", err) + return serializer.Err(serializer.CodeNotFound, "Download record not found", err) } if download.Status >= common.Error { // 如果任务已完成,则删除任务记录 if err := download.Delete(); err != nil { - return serializer.Err(serializer.CodeDBError, "任务记录删除失败", err) + return serializer.DBErr("Failed to delete task record", err) } return serializer.Response{} } @@ -68,11 +68,11 @@ func (service *DownloadTaskService) Delete(c *gin.Context) serializer.Response { // 取消任务 node := cluster.Default.GetNodeByID(download.GetNodeID()) if node == nil { - return serializer.Err(serializer.CodeInternalSetting, "目标节点不可用", err) + return serializer.Err(serializer.CodeNodeOffline, "", err) } if err := node.GetAria2Instance().Cancel(download); err != nil { - return serializer.Err(serializer.CodeNotSet, "操作失败", err) + return serializer.Err(serializer.CodeNotSet, "Operation failed", err) } return serializer.Response{} @@ -86,17 +86,17 @@ func (service *SelectFileService) Select(c *gin.Context) serializer.Response { // 查找下载记录 download, err := model.GetDownloadByGid(c.Param("gid"), user.ID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "下载记录不存在", err) + return serializer.Err(serializer.CodeNotFound, "Download record not found", err) } if download.StatusInfo.BitTorrent.Mode != "multi" || (download.Status != common.Downloading && download.Status != common.Paused) { - return serializer.Err(serializer.CodeNoPermissionErr, "此下载任务无法选取文件", err) + return serializer.ParamErr("You cannot select files for this task", nil) } // 选取下载 node := cluster.Default.GetNodeByID(download.GetNodeID()) if err := node.GetAria2Instance().Select(download, service.Indexes); err != nil { - return serializer.Err(serializer.CodeNotSet, "操作失败", err) + return serializer.Err(serializer.CodeNotSet, "Operation failed", err) } return serializer.Response{} diff --git a/service/callback/upload.go b/service/callback/upload.go index 698d50c..25390bc 100644 --- a/service/callback/upload.go +++ b/service/callback/upload.go @@ -112,7 +112,7 @@ func ProcessCallback(service CallbackProcessService, c *gin.Context) serializer. // 创建文件系统 fs, err := filesystem.NewFileSystemFromCallback(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, err.Error(), err) } defer fs.Recycle() @@ -156,7 +156,7 @@ func (service *OneDriveCallback) PreProcess(c *gin.Context) serializer.Response // 创建文件系统 fs, err := filesystem.NewFileSystemFromCallback(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() @@ -166,7 +166,7 @@ func (service *OneDriveCallback) PreProcess(c *gin.Context) serializer.Response // 获取文件信息 info, err := fs.Handler.(onedrive.Driver).Client.Meta(context.Background(), "", uploadSession.SavePath) if err != nil { - return serializer.Err(serializer.CodeUploadFailed, "文件元信息查询失败", err) + return serializer.Err(serializer.CodeQueryMetaFailed, "", err) } // 验证与回调会话中是否一致 @@ -181,7 +181,7 @@ func (service *OneDriveCallback) PreProcess(c *gin.Context) serializer.Response if isSizeCheckFailed || !strings.EqualFold(info.GetSourcePath(), actualPath) { fs.Handler.(onedrive.Driver).Client.Delete(context.Background(), []string{info.GetSourcePath()}) - return serializer.Err(serializer.CodeUploadFailed, "文件信息不一致", err) + return serializer.Err(serializer.CodeMetaMismatch, "", err) } service.Meta = info return ProcessCallback(service, c) @@ -192,7 +192,7 @@ func (service *COSCallback) PreProcess(c *gin.Context) serializer.Response { // 创建文件系统 fs, err := filesystem.NewFileSystemFromCallback(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() @@ -202,12 +202,12 @@ func (service *COSCallback) PreProcess(c *gin.Context) serializer.Response { // 获取文件信息 info, err := fs.Handler.(cos.Driver).Meta(context.Background(), uploadSession.SavePath) if err != nil { - return serializer.Err(serializer.CodeUploadFailed, "文件信息不一致", err) + return serializer.Err(serializer.CodeMetaMismatch, "", err) } // 验证实际文件信息与回调会话中是否一致 if uploadSession.Size != info.Size || uploadSession.Key != info.CallbackKey { - return serializer.Err(serializer.CodeUploadFailed, "文件信息不一致", err) + return serializer.Err(serializer.CodeMetaMismatch, "", err) } return ProcessCallback(service, c) @@ -218,7 +218,7 @@ func (service *S3Callback) PreProcess(c *gin.Context) serializer.Response { // 创建文件系统 fs, err := filesystem.NewFileSystemFromCallback(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() @@ -228,12 +228,12 @@ func (service *S3Callback) PreProcess(c *gin.Context) serializer.Response { // 获取文件信息 info, err := fs.Handler.(*s3.Driver).Meta(context.Background(), uploadSession.SavePath) if err != nil { - return serializer.Err(serializer.CodeUploadFailed, "文件信息不一致", err) + return serializer.Err(serializer.CodeMetaMismatch, "", err) } // 验证实际文件信息与回调会话中是否一致 if uploadSession.Size != info.Size { - return serializer.Err(serializer.CodeUploadFailed, "文件信息不一致", err) + return serializer.Err(serializer.CodeMetaMismatch, "", err) } return ProcessCallback(service, c) @@ -244,7 +244,7 @@ func (service *UploadCallbackService) PreProcess(c *gin.Context) serializer.Resp // 创建文件系统 fs, err := filesystem.NewFileSystemFromCallback(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() @@ -254,7 +254,7 @@ func (service *UploadCallbackService) PreProcess(c *gin.Context) serializer.Resp // 验证文件大小 if uploadSession.Size != service.Size { fs.Handler.Delete(context.Background(), []string{uploadSession.SavePath}) - return serializer.Err(serializer.CodeUploadFailed, "文件大小不一致", nil) + return serializer.Err(serializer.CodeMetaMismatch, "", err) } return ProcessCallback(service, c) diff --git a/service/explorer/directory.go b/service/explorer/directory.go index 844f812..cd03999 100644 --- a/service/explorer/directory.go +++ b/service/explorer/directory.go @@ -18,7 +18,7 @@ func (service *DirectoryService) ListDirectory(c *gin.Context) serializer.Respon // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() @@ -48,7 +48,7 @@ func (service *DirectoryService) CreateDirectory(c *gin.Context) serializer.Resp // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() diff --git a/service/explorer/file.go b/service/explorer/file.go index 15c88b2..cb4ecfe 100644 --- a/service/explorer/file.go +++ b/service/explorer/file.go @@ -267,7 +267,7 @@ func (service *DownloadService) Download(ctx context.Context, c *gin.Context) se // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() From 906e9857bcd90cd0df7b56054cf9c1292e8d3e61 Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Wed, 20 Jul 2022 20:01:34 +0800 Subject: [PATCH 05/16] i18n: file operation --- pkg/serializer/error.go | 10 +++++++--- routers/controllers/file.go | 4 ++-- service/explorer/file.go | 32 ++++++++++++++--------------- service/explorer/objects.go | 40 ++++++++++++++++++------------------- service/explorer/search.go | 8 ++++---- service/explorer/upload.go | 24 +++++++++++----------- 6 files changed, 61 insertions(+), 57 deletions(-) diff --git a/pkg/serializer/error.go b/pkg/serializer/error.go index 19f520c..0959e03 100644 --- a/pkg/serializer/error.go +++ b/pkg/serializer/error.go @@ -73,11 +73,11 @@ const ( // CodeMasterNotFound 主机节点未注册 CodeMasterNotFound = 40009 // CodeUploadSessionExpired 上传会话已过期 - CodeUploadSessionExpired = 400011 + CodeUploadSessionExpired = 40011 // CodeInvalidChunkIndex 无效的分片序号 - CodeInvalidChunkIndex = 400012 + CodeInvalidChunkIndex = 40012 // CodeInvalidContentLength 无效的正文长度 - CodeInvalidContentLength = 400013 + CodeInvalidContentLength = 40013 // CodeBatchSourceSize 超出批量获取外链限制 CodeBatchSourceSize = 40014 // CodeBatchAria2Size 超出最大 Aria2 任务数量限制 @@ -160,6 +160,10 @@ const ( CodeConflictUploadOngoing = 40054 // 文件信息不一致 CodeMetaMismatch = 40055 + // 不支持该格式的压缩文件 + CodeUnsupportedArchiveType = 40056 + // 可用存储策略发生变化 + CodePolicyChanged = 40057 // CodeDBError 数据库操作失败 CodeDBError = 50001 // CodeEncryptError 加密失败 diff --git a/routers/controllers/file.go b/routers/controllers/file.go index fddc857..c660f88 100644 --- a/routers/controllers/file.go +++ b/routers/controllers/file.go @@ -132,14 +132,14 @@ func Thumb(c *gin.Context) { // 获取文件ID fileID, ok := c.Get("object_id") if !ok { - c.JSON(200, serializer.ParamErr("文件不存在", err)) + c.JSON(200, serializer.Err(serializer.CodeFileNotFound, "", err)) return } // 获取缩略图 resp, err := fs.GetThumb(ctx, fileID.(uint)) if err != nil { - c.JSON(200, serializer.Err(serializer.CodeNotSet, "无法获取缩略图", err)) + c.JSON(200, serializer.Err(serializer.CodeNotSet, "Failed to get thumbnail", err)) return } diff --git a/service/explorer/file.go b/service/explorer/file.go index cb4ecfe..75657cc 100644 --- a/service/explorer/file.go +++ b/service/explorer/file.go @@ -51,7 +51,7 @@ func (service *SingleFileService) Create(c *gin.Context) serializer.Response { // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() @@ -101,21 +101,21 @@ func (service *SlaveListService) List(c *gin.Context) serializer.Response { func (service *ArchiveService) DownloadArchived(ctx context.Context, c *gin.Context) serializer.Response { userRaw, exist := cache.Get("archive_user_" + service.ID) if !exist { - return serializer.Err(404, "归档会话不存在", nil) + return serializer.Err(serializer.CodeNotFound, "Archive session not exist", nil) } user := userRaw.(model.User) // 创建文件系统 fs, err := filesystem.NewFileSystem(&user) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() // 查找打包的临时文件 archiveSession, exist := cache.Get("archive_" + service.ID) if !exist { - return serializer.Err(404, "归档会话不存在", nil) + return serializer.Err(serializer.CodeNotFound, "Archive session not exist", nil) } // 开始打包 @@ -126,7 +126,7 @@ func (service *ArchiveService) DownloadArchived(ctx context.Context, c *gin.Cont ctx = context.WithValue(ctx, fsctx.GinCtx, c) err = fs.Compress(ctx, c.Writer, items.Dirs, items.Items, true) if err != nil { - return serializer.Err(serializer.CodeNotSet, "无法创建压缩文件", err) + return serializer.Err(serializer.CodeNotSet, "Failed to compress file", err) } return serializer.Response{ @@ -138,7 +138,7 @@ func (service *ArchiveService) DownloadArchived(ctx context.Context, c *gin.Cont func (service *FileAnonymousGetService) Download(ctx context.Context, c *gin.Context) serializer.Response { fs, err := filesystem.NewAnonymousFileSystem() if err != nil { - return serializer.Err(serializer.CodeGroupNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() @@ -167,7 +167,7 @@ func (service *FileAnonymousGetService) Download(ctx context.Context, c *gin.Con func (service *FileAnonymousGetService) Source(ctx context.Context, c *gin.Context) serializer.Response { fs, err := filesystem.NewAnonymousFileSystem() if err != nil { - return serializer.Err(serializer.CodeGroupNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() @@ -243,7 +243,7 @@ func (service *FileIDService) CreateDownloadSession(ctx context.Context, c *gin. // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() @@ -274,7 +274,7 @@ func (service *DownloadService) Download(ctx context.Context, c *gin.Context) se // 查找打包的临时文件 file, exist := cache.Get("download_" + service.ID) if !exist { - return serializer.Err(404, "文件下载会话不存在", nil) + return serializer.Err(serializer.CodeNotFound, "Download session not exist", nil) } fs.FileTarget = []model.File{file.(model.File)} @@ -308,7 +308,7 @@ func (service *FileIDService) PreviewContent(ctx context.Context, c *gin.Context // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() @@ -327,7 +327,7 @@ func (service *FileIDService) PreviewContent(ctx context.Context, c *gin.Context path := ctx.Value(fsctx.PathCtx).(string) err := fs.ResetFileIfNotExist(ctx, path) if err != nil { - return serializer.Err(serializer.CodeNotFound, err.Error(), err) + return serializer.Err(serializer.CodeFileNotFound, err.Error(), err) } objectID = uint(0) } @@ -371,7 +371,7 @@ func (service *FileIDService) PutContent(ctx context.Context, c *gin.Context) se fileSize, err := strconv.ParseUint(c.Request.Header.Get("Content-Length"), 10, 64) if err != nil { - return serializer.ParamErr("无法解析文件尺寸", err) + return serializer.ParamErr("Invalid content-length value", err) } fileData := fsctx.FileStream{ @@ -384,7 +384,7 @@ func (service *FileIDService) PutContent(ctx context.Context, c *gin.Context) se // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } uploadCtx := context.WithValue(ctx, fsctx.GinCtx, c) @@ -392,7 +392,7 @@ func (service *FileIDService) PutContent(ctx context.Context, c *gin.Context) se fileID, _ := c.Get("object_id") originFile, _ := model.GetFilesByIDs([]uint{fileID.(uint)}, fs.User.ID) if len(originFile) == 0 { - return serializer.Err(404, "文件不存在", nil) + return serializer.Err(serializer.CodeFileNotFound, "", nil) } fileData.Name = originFile[0].Name @@ -433,12 +433,12 @@ func (service *FileIDService) PutContent(ctx context.Context, c *gin.Context) se 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) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() if len(s.Raw().Items) > fs.User.Group.OptionsSerialized.SourceBatchSize { - return serializer.Err(serializer.CodeBatchSourceSize, "超出批量获取外链的最大数量限制", err) + return serializer.Err(serializer.CodeBatchSourceSize, "", err) } res := make([]serializer.Sources, 0, len(s.Raw().Items)) diff --git a/service/explorer/objects.go b/service/explorer/objects.go index 7aeccdc..fea160b 100644 --- a/service/explorer/objects.go +++ b/service/explorer/objects.go @@ -102,30 +102,30 @@ func (service *ItemDecompressService) CreateDecompressTask(c *gin.Context) seria // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() // 检查用户组权限 if !fs.User.Group.OptionsSerialized.ArchiveTask { - return serializer.Err(serializer.CodeGroupNotAllowed, "当前用户组无法进行此操作", nil) + return serializer.Err(serializer.CodeGroupNotAllowed, "", nil) } // 存放目录是否存在 if exist, _ := fs.IsPathExist(service.Dst); !exist { - return serializer.Err(serializer.CodeNotFound, "存放路径不存在", nil) + return serializer.Err(serializer.CodeParentNotExist, "", nil) } // 压缩包是否存在 exist, file := fs.IsFileExist(service.Src) if !exist { - return serializer.Err(serializer.CodeNotFound, "文件不存在", nil) + return serializer.Err(serializer.CodeFileNotFound, "", nil) } // 文件尺寸限制 if fs.User.Group.OptionsSerialized.DecompressSize != 0 && file.Size > fs.User.Group. OptionsSerialized.DecompressSize { - return serializer.Err(serializer.CodeParamErr, "文件太大", nil) + return serializer.Err(serializer.CodeFileTooLarge, "", nil) } // 支持的压缩格式后缀 @@ -140,13 +140,13 @@ func (service *ItemDecompressService) CreateDecompressTask(c *gin.Context) seria } } if !matched { - return serializer.Err(serializer.CodeParamErr, "不支持该格式的压缩文件", nil) + return serializer.Err(serializer.CodeUnsupportedArchiveType, "", nil) } // 创建任务 job, err := task.NewDecompressTask(fs.User, service.Src, service.Dst, service.Encoding) if err != nil { - return serializer.Err(serializer.CodeNotSet, "任务创建失败", err) + return serializer.Err(serializer.CodeCreateTaskError, "", err) } task.TaskPoll.Submit(job) @@ -159,13 +159,13 @@ func (service *ItemCompressService) CreateCompressTask(c *gin.Context) serialize // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() // 检查用户组权限 if !fs.User.Group.OptionsSerialized.ArchiveTask { - return serializer.Err(serializer.CodeGroupNotAllowed, "当前用户组无法进行此操作", nil) + return serializer.Err(serializer.CodeGroupNotAllowed, "", nil) } // 补齐压缩文件扩展名(如果没有) @@ -175,30 +175,30 @@ func (service *ItemCompressService) CreateCompressTask(c *gin.Context) serialize // 存放目录是否存在,是否重名 if exist, _ := fs.IsPathExist(service.Dst); !exist { - return serializer.Err(serializer.CodeNotFound, "存放路径不存在", nil) + return serializer.Err(serializer.CodeParentNotExist, "", nil) } if exist, _ := fs.IsFileExist(path.Join(service.Dst, service.Name)); exist { - return serializer.ParamErr("名为 "+service.Name+" 的文件已存在", nil) + return serializer.ParamErr("File "+service.Name+" already exist", nil) } // 检查文件名合法性 if !fs.ValidateLegalName(context.Background(), service.Name) { - return serializer.ParamErr("文件名非法", nil) + return serializer.Err(serializer.CodeIllegalObjectName, "", nil) } if !fs.ValidateExtension(context.Background(), service.Name) { - return serializer.ParamErr("不允许存储此扩展名的文件", nil) + return serializer.Err(serializer.CodeFileTypeNotAllowed, "", nil) } // 递归列出待压缩子目录 folders, err := model.GetRecursiveChildFolder(service.Src.Raw().Dirs, fs.User.ID, true) if err != nil { - return serializer.Err(serializer.CodeDBError, "无法列出子目录", err) + return serializer.DBErr("Failed to list folders", err) } // 列出所有待压缩文件 files, err := model.GetChildFilesOfFolders(&folders) if err != nil { - return serializer.Err(serializer.CodeDBError, "无法列出子文件", err) + return serializer.DBErr("Failed to list files", err) } // 计算待压缩文件大小 @@ -210,21 +210,21 @@ func (service *ItemCompressService) CreateCompressTask(c *gin.Context) serialize // 文件尺寸限制 if fs.User.Group.OptionsSerialized.CompressSize != 0 && totalSize > fs.User.Group. OptionsSerialized.CompressSize { - return serializer.Err(serializer.CodeParamErr, "文件太大", nil) + return serializer.Err(serializer.CodeFileTooLarge, "", nil) } // 按照平均压缩率计算用户空间是否足够 compressRatio := 0.4 spaceNeeded := uint64(math.Round(float64(totalSize) * compressRatio)) if fs.User.GetRemainingCapacity() < spaceNeeded { - return serializer.Err(serializer.CodeParamErr, "剩余空间不足", err) + return serializer.Err(serializer.CodeInsufficientCapacity, "", err) } // 创建任务 job, err := task.NewCompressTask(fs.User, path.Join(service.Dst, service.Name), service.Src.Raw().Dirs, service.Src.Raw().Items) if err != nil { - return serializer.Err(serializer.CodeNotSet, "任务创建失败", err) + return serializer.Err(serializer.CodeCreateTaskError, "", err) } task.TaskPoll.Submit(job) @@ -237,13 +237,13 @@ func (service *ItemIDService) Archive(ctx context.Context, c *gin.Context) seria // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() // 检查用户组权限 if !fs.User.Group.OptionsSerialized.ArchiveDownload { - return serializer.Err(serializer.CodeGroupNotAllowed, "当前用户组无法进行此操作", nil) + return serializer.Err(serializer.CodeGroupNotAllowed, "", nil) } // 创建打包下载会话 diff --git a/service/explorer/search.go b/service/explorer/search.go index 6a2b92f..72b7afa 100644 --- a/service/explorer/search.go +++ b/service/explorer/search.go @@ -23,14 +23,14 @@ func (service *ItemSearchService) Search(c *gin.Context) serializer.Response { // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() if service.Path != "" { ok, parent := fs.IsPathExist(service.Path) if !ok { - return serializer.Err(serializer.CodeParentNotExist, "Cannot find parent folder", nil) + return serializer.Err(serializer.CodeParentNotExist, "", nil) } fs.Root = parent @@ -60,9 +60,9 @@ func (service *ItemSearchService) Search(c *gin.Context) serializer.Response { } } } - return serializer.Err(serializer.CodeNotFound, "标签不存在", nil) + return serializer.Err(serializer.CodeNotFound, "", nil) default: - return serializer.ParamErr("未知搜索类型", nil) + return serializer.ParamErr("Unknown search type", nil) } } diff --git a/service/explorer/upload.go b/service/explorer/upload.go index ced4841..5e6dc4e 100644 --- a/service/explorer/upload.go +++ b/service/explorer/upload.go @@ -33,13 +33,13 @@ func (service *CreateUploadSessionService) Create(ctx context.Context, c *gin.Co // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } // 取得存储策略的ID rawID, err := hashid.DecodeHashID(service.PolicyID, hashid.PolicyID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "存储策略不存在", err) + return serializer.Err(serializer.CodePolicyNotExist, "", err) } if fs.Policy.ID != rawID { @@ -77,7 +77,7 @@ type UploadService struct { func (service *UploadService) LocalUpload(ctx context.Context, c *gin.Context) serializer.Response { uploadSessionRaw, ok := cache.Get(filesystem.UploadSessionCachePrefix + service.ID) if !ok { - return serializer.Err(serializer.CodeUploadSessionExpired, "LocalUpload session expired or not exist", nil) + return serializer.Err(serializer.CodeUploadSessionExpired, "", nil) } uploadSession := uploadSessionRaw.(serializer.UploadSession) @@ -88,23 +88,23 @@ func (service *UploadService) LocalUpload(ctx context.Context, c *gin.Context) s } if uploadSession.UID != fs.User.ID { - return serializer.Err(serializer.CodeUploadSessionExpired, "Local upload session expired or not exist", nil) + return serializer.Err(serializer.CodeUploadSessionExpired, "", nil) } // 查找上传会话创建的占位文件 file, err := model.GetFilesByUploadSession(service.ID, fs.User.ID) if err != nil { - return serializer.Err(serializer.CodeUploadSessionExpired, "Local upload session file placeholder not exist", err) + return serializer.Err(serializer.CodeUploadSessionExpired, "", err) } // 重设 fs 存储策略 if !uploadSession.Policy.IsTransitUpload(uploadSession.Size) { - return serializer.Err(serializer.CodePolicyNotAllowed, "Storage policy not supported", err) + return serializer.Err(serializer.CodePolicyNotAllowed, "", err) } fs.Policy = &uploadSession.Policy if err := fs.DispatchHandler(); err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, "Unknown storage policy", err) + return serializer.Err(serializer.CodePolicyNotExist, "", err) } expectedSizeStart := file.Size @@ -118,7 +118,7 @@ func (service *UploadService) LocalUpload(ctx context.Context, c *gin.Context) s } if expectedSizeStart > actualSizeStart { - util.Log().Info("尝试上传覆盖分片[%d] Start=%d", service.Index, actualSizeStart) + util.Log().Info("Trying to overwrite chunk[%d] Start=%d", service.Index, actualSizeStart) } return processChunkUpload(ctx, c, fs, &uploadSession, service.Index, file, fsctx.Append) @@ -128,7 +128,7 @@ func (service *UploadService) LocalUpload(ctx context.Context, c *gin.Context) s func (service *UploadService) SlaveUpload(ctx context.Context, c *gin.Context) serializer.Response { uploadSessionRaw, ok := cache.Get(filesystem.UploadSessionCachePrefix + service.ID) if !ok { - return serializer.Err(serializer.CodeUploadSessionExpired, "Slave upload session expired or not exist", nil) + return serializer.Err(serializer.CodeUploadSessionExpired, "", nil) } uploadSession := uploadSessionRaw.(serializer.UploadSession) @@ -226,14 +226,14 @@ func (service *UploadSessionService) Delete(ctx context.Context, c *gin.Context) // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() // 查找需要删除的上传会话的占位文件 file, err := model.GetFilesByUploadSession(service.ID, fs.User.ID) if err != nil { - return serializer.Err(serializer.CodeUploadSessionExpired, "Local Upload session file placeholder not exist", err) + return serializer.Err(serializer.CodeUploadSessionExpired, "", err) } // 删除文件 @@ -271,7 +271,7 @@ func DeleteAllUploadSession(ctx context.Context, c *gin.Context) serializer.Resp // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() From 96daed26b4453093d9d1a4dfe8d9ee334a7338af Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Wed, 20 Jul 2022 20:03:41 +0800 Subject: [PATCH 06/16] i18n: objects / share / slave / tag operations --- pkg/filesystem/errors.go | 1 + pkg/filesystem/manage.go | 8 ++++---- pkg/serializer/error.go | 8 ++++++++ routers/controllers/share.go | 4 ++-- service/admin/policy.go | 6 +++--- service/aria2/add.go | 4 ++-- service/aria2/manage.go | 8 ++++---- service/explorer/file.go | 4 ++-- service/explorer/objects.go | 28 ++++++++++++++-------------- service/explorer/slave.go | 16 ++++++++-------- service/explorer/tag.go | 6 +++--- service/explorer/upload.go | 6 +++--- service/node/fabric.go | 2 +- service/share/manage.go | 16 ++++++++-------- service/share/visit.go | 34 +++++++++++++++++----------------- 15 files changed, 80 insertions(+), 71 deletions(-) diff --git a/pkg/filesystem/errors.go b/pkg/filesystem/errors.go index 6ca9e3a..303d0d5 100644 --- a/pkg/filesystem/errors.go +++ b/pkg/filesystem/errors.go @@ -22,4 +22,5 @@ var ( ErrIO = serializer.NewError(serializer.CodeIOFailed, "Failed to read file data", nil) ErrDBListObjects = serializer.NewError(serializer.CodeDBError, "Failed to list object records", nil) ErrDBDeleteObjects = serializer.NewError(serializer.CodeDBError, "Failed to delete object records", nil) + ErrOneObjectOnly = serializer.ParamErr("You can only copy one object at the same time", nil) ) diff --git a/pkg/filesystem/manage.go b/pkg/filesystem/manage.go index f1b9ad6..34c54c7 100644 --- a/pkg/filesystem/manage.go +++ b/pkg/filesystem/manage.go @@ -73,7 +73,7 @@ func (fs *FileSystem) Copy(ctx context.Context, dirs, files []uint, src, dst str if len(dirs) > 0 { subFileSizes, err := srcFolder.CopyFolderTo(dirs[0], dstFolder) if err != nil { - return serializer.NewError(serializer.CodeDBError, "操作失败,可能有重名冲突", err) + return ErrObjectNotExist.WithError(err) } newUsedStorage += subFileSizes } @@ -82,7 +82,7 @@ func (fs *FileSystem) Copy(ctx context.Context, dirs, files []uint, src, dst str if len(files) > 0 { subFileSizes, err := srcFolder.MoveOrCopyFileTo(files, dstFolder, true) if err != nil { - return serializer.NewError(serializer.CodeDBError, "操作失败,可能有重名冲突", err) + return ErrObjectNotExist.WithError(err) } newUsedStorage += subFileSizes } @@ -106,13 +106,13 @@ func (fs *FileSystem) Move(ctx context.Context, dirs, files []uint, src, dst str // 处理目录及子文件移动 err := srcFolder.MoveFolderTo(dirs, dstFolder) if err != nil { - return serializer.NewError(serializer.CodeDBError, "操作失败,可能有重名冲突", err) + return ErrFileExisted.WithError(err) } // 处理文件移动 _, err = srcFolder.MoveOrCopyFileTo(files, dstFolder, false) if err != nil { - return serializer.NewError(serializer.CodeDBError, "操作失败,可能有重名冲突", err) + return ErrFileExisted.WithError(err) } // 移动文件 diff --git a/pkg/serializer/error.go b/pkg/serializer/error.go index 0959e03..82c37e1 100644 --- a/pkg/serializer/error.go +++ b/pkg/serializer/error.go @@ -164,6 +164,14 @@ const ( CodeUnsupportedArchiveType = 40056 // 可用存储策略发生变化 CodePolicyChanged = 40057 + // 分享链接无效 + CodeShareLinkNotFound = 40058 + // 不能转存自己的分享 + CodeSaveOwnShare = 40059 + // 从机无法向主机发送回调请求 + CodeSlavePingMaster = 40060 + // Cloudreve 版本不一致 + CodeVersionMismatch = 40061 // CodeDBError 数据库操作失败 CodeDBError = 50001 // CodeEncryptError 加密失败 diff --git a/routers/controllers/share.go b/routers/controllers/share.go index f8e7f49..8795c1e 100644 --- a/routers/controllers/share.go +++ b/routers/controllers/share.go @@ -142,13 +142,13 @@ func PreviewShareReadme(c *gin.Context) { allowFileName := []string{"readme.txt", "readme.md"} fileName := strings.ToLower(path.Base(service.Path)) if !util.ContainsString(allowFileName, fileName) { - c.JSON(200, serializer.ParamErr("非README文件", nil)) + c.JSON(200, serializer.ParamErr("Not a README file", nil)) } // 必须是目录分享 if shareCtx, ok := c.Get("share"); ok { if !shareCtx.(*model.Share).IsDir { - c.JSON(200, serializer.ParamErr("此分享无自述文件", nil)) + c.JSON(200, serializer.ParamErr("This share has no README file", nil)) } } diff --git a/service/admin/policy.go b/service/admin/policy.go index f9d40a8..3207a7c 100644 --- a/service/admin/policy.go +++ b/service/admin/policy.go @@ -194,7 +194,7 @@ func (service *PolicyService) AddCORS() serializer.Response { func (service *SlavePingService) Test() serializer.Response { master, err := url.Parse(service.Callback) if err != nil { - return serializer.ParamErr("无法解析主机站点地址,请检查主机 参数设置 - 站点信息 - 站点URL设置,"+err.Error(), nil) + return serializer.ParamErr("Failed to parse Master site url: "+err.Error(), nil) } controller, _ := url.Parse("/api/v3/site/ping") @@ -208,7 +208,7 @@ func (service *SlavePingService) Test() serializer.Response { ).DecodeResponse() if err != nil { - return serializer.ParamErr("从机无法向主机发送回调请求,请检查主机端 参数设置 - 站点信息 - 站点URL设置,并确保从机可以连接到此地址,"+err.Error(), nil) + return serializer.Err(serializer.CodeSlavePingMaster, err.Error(), nil) } version := conf.BackendVersion @@ -216,7 +216,7 @@ func (service *SlavePingService) Test() serializer.Response { version += "-pro" } if res.Data.(string) != version { - return serializer.ParamErr("Cloudreve版本不一致,主机:"+res.Data.(string)+",从机:"+version, nil) + return serializer.Err(serializer.CodeVersionMismatch, "Master: "+res.Data.(string)+", Slave: "+version, nil) } return serializer.Response{} diff --git a/service/aria2/add.go b/service/aria2/add.go index 52b5964..816c57b 100644 --- a/service/aria2/add.go +++ b/service/aria2/add.go @@ -136,14 +136,14 @@ func Add(c *gin.Context, service *serializer.SlaveAria2Call) serializer.Response // 创建任务 gid, err := caller.(common.Aria2).CreateTask(service.Task, service.GroupOptions) if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "无法创建离线下载任务", err) + return serializer.Err(serializer.CodeInternalSetting, "Failed to create aria2 task", err) } // 创建事件通知回调 siteID, _ := c.Get("MasterSiteID") mq.GlobalMQ.SubscribeCallback(gid, func(message mq.Message) { if err := cluster.DefaultController.SendNotification(siteID.(string), message.TriggeredBy, message); err != nil { - util.Log().Warning("无法发送离线下载任务状态变更通知, %s", err) + util.Log().Warning("Failed to send remote download task status change notifications: %s", err) } }) diff --git a/service/aria2/manage.go b/service/aria2/manage.go index a8cd7f6..6344ddd 100644 --- a/service/aria2/manage.go +++ b/service/aria2/manage.go @@ -110,7 +110,7 @@ func SlaveStatus(c *gin.Context, service *serializer.SlaveAria2Call) serializer. // 查询任务 status, err := caller.(common.Aria2).Status(service.Task) if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "离线下载任务查询失败", err) + return serializer.Err(serializer.CodeInternalSetting, "Failed to query remote download task status", err) } return serializer.NewResponseWithGobData(status) @@ -124,7 +124,7 @@ func SlaveCancel(c *gin.Context, service *serializer.SlaveAria2Call) serializer. // 查询任务 err := caller.(common.Aria2).Cancel(service.Task) if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "任务取消失败", err) + return serializer.Err(serializer.CodeInternalSetting, "Failed to cancel task", err) } return serializer.Response{} @@ -138,7 +138,7 @@ func SlaveSelect(c *gin.Context, service *serializer.SlaveAria2Call) serializer. // 查询任务 err := caller.(common.Aria2).Select(service.Task, service.Files) if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "任务选取失败", err) + return serializer.Err(serializer.CodeInternalSetting, "Failed to select files", err) } return serializer.Response{} @@ -152,7 +152,7 @@ func SlaveDeleteTemp(c *gin.Context, service *serializer.SlaveAria2Call) seriali // 查询任务 err := caller.(common.Aria2).DeleteTempFile(service.Task) if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "临时文件删除失败", err) + return serializer.Err(serializer.CodeInternalSetting, "Failed to delete temp files", err) } return serializer.Response{} diff --git a/service/explorer/file.go b/service/explorer/file.go index 75657cc..aea0fbf 100644 --- a/service/explorer/file.go +++ b/service/explorer/file.go @@ -84,13 +84,13 @@ func (service *SlaveListService) List(c *gin.Context) serializer.Response { // 创建文件系统 fs, err := filesystem.NewAnonymousFileSystem() if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() objects, err := fs.Handler.List(context.Background(), service.Path, service.Recursive) if err != nil { - return serializer.Err(serializer.CodeIOFailed, "无法列取文件", err) + return serializer.Err(serializer.CodeIOFailed, "Cannot list files", err) } res, _ := json.Marshal(objects) diff --git a/service/explorer/objects.go b/service/explorer/objects.go index fea160b..5099c7e 100644 --- a/service/explorer/objects.go +++ b/service/explorer/objects.go @@ -290,7 +290,7 @@ func (service *ItemMoveService) Move(ctx context.Context, c *gin.Context) serial // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() @@ -311,13 +311,13 @@ func (service *ItemMoveService) Move(ctx context.Context, c *gin.Context) serial func (service *ItemMoveService) Copy(ctx context.Context, c *gin.Context) serializer.Response { // 复制操作只能对一个目录或文件对象进行操作 if len(service.Src.Items)+len(service.Src.Dirs) > 1 { - return serializer.ParamErr("只能复制一个对象", nil) + return filesystem.ErrOneObjectOnly } // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() @@ -337,13 +337,13 @@ func (service *ItemMoveService) Copy(ctx context.Context, c *gin.Context) serial func (service *ItemRenameService) Rename(ctx context.Context, c *gin.Context) serializer.Response { // 重命名作只能对一个目录或文件对象进行操作 if len(service.Src.Items)+len(service.Src.Dirs) > 1 { - return serializer.ParamErr("只能操作一个对象", nil) + return filesystem.ErrOneObjectOnly } // 创建文件系统 fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() @@ -370,12 +370,12 @@ func (service *ItemPropertyService) GetProperty(ctx context.Context, c *gin.Cont if !service.IsFolder { res, err := hashid.DecodeHashID(service.ID, hashid.FileID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "对象不存在", err) + return serializer.Err(serializer.CodeNotFound, "", err) } file, err := model.GetFilesByIDs([]uint{res}, user.ID) if err != nil { - return serializer.DBErr("找不到文件", err) + return serializer.DBErr("Failed to query file records", err) } props.CreatedAt = file[0].CreatedAt @@ -387,11 +387,11 @@ func (service *ItemPropertyService) GetProperty(ctx context.Context, c *gin.Cont if service.TraceRoot { parent, err := model.GetFoldersByIDs([]uint{file[0].FolderID}, user.ID) if err != nil { - return serializer.DBErr("找不到父目录", err) + return serializer.DBErr("Parent folder record not exist", err) } if err := parent[0].TraceRoot(); err != nil { - return serializer.DBErr("无法溯源父目录", err) + return serializer.DBErr("Failed to trace root folder", err) } props.Path = path.Join(parent[0].Position, parent[0].Name) @@ -399,12 +399,12 @@ func (service *ItemPropertyService) GetProperty(ctx context.Context, c *gin.Cont } else { res, err := hashid.DecodeHashID(service.ID, hashid.FolderID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "对象不存在", err) + return serializer.Err(serializer.CodeNotFound, "", err) } folder, err := model.GetFoldersByIDs([]uint{res}, user.ID) if err != nil { - return serializer.DBErr("找不到目录", err) + return serializer.DBErr("Failed to query folder records", err) } props.CreatedAt = folder[0].CreatedAt @@ -422,14 +422,14 @@ func (service *ItemPropertyService) GetProperty(ctx context.Context, c *gin.Cont childFolders, err := model.GetRecursiveChildFolder([]uint{folder[0].ID}, user.ID, true) if err != nil { - return serializer.DBErr("无法列取子目录", err) + return serializer.DBErr("Failed to list child folders", err) } props.ChildFolderNum = len(childFolders) - 1 // 统计子文件 files, err := model.GetChildFilesOfFolders(&childFolders) if err != nil { - return serializer.DBErr("无法列取子文件", err) + return serializer.DBErr("Failed to list child files", err) } // 统计子文件个数和大小 @@ -441,7 +441,7 @@ func (service *ItemPropertyService) GetProperty(ctx context.Context, c *gin.Cont // 查找父目录 if service.TraceRoot { if err := folder[0].TraceRoot(); err != nil { - return serializer.DBErr("无法溯源父目录", err) + return serializer.DBErr("Failed to list child folders", err) } props.Path = folder[0].Position diff --git a/service/explorer/slave.go b/service/explorer/slave.go index f2ba487..1435640 100644 --- a/service/explorer/slave.go +++ b/service/explorer/slave.go @@ -49,14 +49,14 @@ func (service *SlaveDownloadService) ServeFile(ctx context.Context, c *gin.Conte // 创建文件系统 fs, err := filesystem.NewAnonymousFileSystem() if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() // 解码文件路径 fileSource, err := base64.RawURLEncoding.DecodeString(service.PathEncoded) if err != nil { - return serializer.ParamErr("无法解析的文件地址", err) + return serializer.Err(serializer.CodeFileNotFound, "", err) } // 根据URL里的信息创建一个文件对象和用户对象 @@ -97,7 +97,7 @@ func (service *SlaveFilesService) Delete(ctx context.Context, c *gin.Context) se // 创建文件系统 fs, err := filesystem.NewAnonymousFileSystem() if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() @@ -110,7 +110,7 @@ func (service *SlaveFilesService) Delete(ctx context.Context, c *gin.Context) se return serializer.Response{ Code: serializer.CodeNotFullySuccess, Data: string(data), - Msg: fmt.Sprintf("有 %d 个文件未能成功删除", len(failed)), + Msg: fmt.Sprintf("Failed to delete %d files(s)", len(failed)), Error: err.Error(), } } @@ -122,21 +122,21 @@ func (service *SlaveFileService) Thumb(ctx context.Context, c *gin.Context) seri // 创建文件系统 fs, err := filesystem.NewAnonymousFileSystem() if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() // 解码文件路径 fileSource, err := base64.RawURLEncoding.DecodeString(service.PathEncoded) if err != nil { - return serializer.ParamErr("无法解析的文件地址", err) + return serializer.Err(serializer.CodeFileNotFound, "", err) } fs.FileTarget = []model.File{{SourceName: string(fileSource), PicInfo: "1,1"}} // 获取缩略图 resp, err := fs.GetThumb(ctx, 0) if err != nil { - return serializer.Err(serializer.CodeNotSet, "无法获取缩略图", err) + return serializer.Err(serializer.CodeNotSet, "Failed to get thumb", err) } defer resp.Content.Close() @@ -156,7 +156,7 @@ func CreateTransferTask(c *gin.Context, req *serializer.SlaveTransferReq) serial if err := cluster.DefaultController.SubmitTask(job.MasterID, job, req.Hash(job.MasterID), func(job interface{}) { task.TaskPoll.Submit(job.(task.Job)) }); err != nil { - return serializer.Err(serializer.CodeInternalSetting, "任务创建失败", err) + return serializer.Err(serializer.CodeCreateTaskError, "", err) } return serializer.Response{} diff --git a/service/explorer/tag.go b/service/explorer/tag.go index eb49f7d..02e324f 100644 --- a/service/explorer/tag.go +++ b/service/explorer/tag.go @@ -32,7 +32,7 @@ type TagService struct { func (service *TagService) Delete(c *gin.Context, user *model.User) serializer.Response { id, _ := c.Get("object_id") if err := model.DeleteTagByID(id.(uint), user.ID); err != nil { - return serializer.Err(serializer.CodeDBError, "Failed to delete a tag", err) + return serializer.DBErr("Failed to delete a tag", err) } return serializer.Response{} } @@ -49,7 +49,7 @@ func (service *LinkTagCreateService) Create(c *gin.Context, user *model.User) se } id, err := tag.Create() if err != nil { - return serializer.Err(serializer.CodeDBError, "Failed to create a tag", err) + return serializer.DBErr("Failed to create a tag", err) } return serializer.Response{ @@ -79,7 +79,7 @@ func (service *FilterTagCreateService) Create(c *gin.Context, user *model.User) } id, err := tag.Create() if err != nil { - return serializer.Err(serializer.CodeDBError, "Failed to create a tag", err) + return serializer.DBErr("Failed to create a tag", err) } return serializer.Response{ diff --git a/service/explorer/upload.go b/service/explorer/upload.go index 5e6dc4e..b3501be 100644 --- a/service/explorer/upload.go +++ b/service/explorer/upload.go @@ -135,7 +135,7 @@ func (service *UploadService) SlaveUpload(ctx context.Context, c *gin.Context) s fs, err := filesystem.NewAnonymousFileSystem() if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } fs.Handler = local.Driver{} @@ -249,13 +249,13 @@ func (service *UploadSessionService) SlaveDelete(ctx context.Context, c *gin.Con // 创建文件系统 fs, err := filesystem.NewAnonymousFileSystem() if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() session, ok := cache.Get(filesystem.UploadSessionCachePrefix + service.ID) if !ok { - return serializer.Err(serializer.CodeUploadSessionExpired, "Slave Upload session file placeholder not exist", nil) + return serializer.Err(serializer.CodeUploadSessionExpired, "", nil) } if _, err := fs.Handler.Delete(ctx, []string{session.(serializer.UploadSession).SavePath}); err != nil { diff --git a/service/node/fabric.go b/service/node/fabric.go index 9ad978d..a1b6212 100644 --- a/service/node/fabric.go +++ b/service/node/fabric.go @@ -47,7 +47,7 @@ func (s *SlaveNotificationService) HandleSlaveNotificationPush(c *gin.Context) s func (s *OneDriveCredentialService) Get(c *gin.Context) serializer.Response { policy, err := model.GetPolicyByID(s.PolicyID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "Cannot found storage policy", err) + return serializer.Err(serializer.CodePolicyNotExist, "", err) } client, err := onedrive.NewClient(&policy) diff --git a/service/share/manage.go b/service/share/manage.go index bd826e1..9daccdb 100644 --- a/service/share/manage.go +++ b/service/share/manage.go @@ -30,11 +30,11 @@ type ShareUpdateService struct { func (service *Service) Delete(c *gin.Context, user *model.User) serializer.Response { share := model.GetShareByHashID(c.Param("id")) if share == nil || share.Creator().ID != user.ID { - return serializer.Err(serializer.CodeNotFound, "分享不存在", nil) + return serializer.Err(serializer.CodeShareLinkNotFound, "", nil) } if err := share.Delete(); err != nil { - return serializer.Err(serializer.CodeDBError, "分享删除失败", err) + return serializer.DBErr("Failed to delete share record", err) } return serializer.Response{} @@ -49,13 +49,13 @@ func (service *ShareUpdateService) Update(c *gin.Context) serializer.Response { case "password": err := share.Update(map[string]interface{}{"password": service.Value}) if err != nil { - return serializer.Err(serializer.CodeDBError, "无法更新分享密码", err) + return serializer.DBErr("Failed to update share record", err) } case "preview_enabled": value := service.Value == "true" err := share.Update(map[string]interface{}{"preview_enabled": value}) if err != nil { - return serializer.Err(serializer.CodeDBError, "无法更新分享属性", err) + return serializer.DBErr("Failed to update share record", err) } return serializer.Response{ Data: value, @@ -73,7 +73,7 @@ func (service *ShareCreateService) Create(c *gin.Context) serializer.Response { // 是否拥有权限 if !user.Group.ShareEnabled { - return serializer.Err(serializer.CodeNoPermissionErr, "您无权创建分享链接", nil) + return serializer.Err(serializer.CodeGroupNotAllowed, "", nil) } // 源对象真实ID @@ -88,7 +88,7 @@ func (service *ShareCreateService) Create(c *gin.Context) serializer.Response { sourceID, err = hashid.DecodeHashID(service.SourceID, hashid.FileID) } if err != nil { - return serializer.Err(serializer.CodeNotFound, "原始资源不存在", nil) + return serializer.Err(serializer.CodeNotFound, "", nil) } // 对象是否存在 @@ -109,7 +109,7 @@ func (service *ShareCreateService) Create(c *gin.Context) serializer.Response { } } if !exist { - return serializer.Err(serializer.CodeNotFound, "原始资源不存在", nil) + return serializer.Err(serializer.CodeNotFound, "", nil) } newShare := model.Share{ @@ -132,7 +132,7 @@ func (service *ShareCreateService) Create(c *gin.Context) serializer.Response { // 创建分享 id, err := newShare.Create() if err != nil { - return serializer.Err(serializer.CodeDBError, "分享链接创建失败", err) + return serializer.DBErr("Failed to create share link record", err) } // 获取分享的唯一id diff --git a/service/share/visit.go b/service/share/visit.go index 2e07238..86627da 100644 --- a/service/share/visit.go +++ b/service/share/visit.go @@ -54,7 +54,7 @@ func (service *ShareUserGetService) Get(c *gin.Context) serializer.Response { userID, _ := c.Get("object_id") user, err := model.GetActiveUserByID(userID.(uint)) if err != nil || user.OptionsSerialized.ProfileOff { - return serializer.Err(serializer.CodeNotFound, "用户不存在", err) + return serializer.Err(serializer.CodeNotFound, "", err) } // 列出分享 @@ -153,14 +153,14 @@ func (service *Service) CreateDownloadSession(c *gin.Context) serializer.Respons // 创建文件系统 fs, err := filesystem.NewFileSystem(user) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.DBErr("Failed to update share record", err) } defer fs.Recycle() // 重设文件系统处理目标为源文件 err = fs.SetTargetByInterface(share.Source()) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, "源文件不存在", err) + return serializer.Err(serializer.CodeFileNotFound, "", err) } ctx := context.Background() @@ -230,17 +230,17 @@ func (service *Service) List(c *gin.Context) serializer.Response { share := shareCtx.(*model.Share) if !share.IsDir { - return serializer.ParamErr("此分享无法列目录", nil) + return serializer.ParamErr("This is not a shared folder", nil) } if !path.IsAbs(service.Path) { - return serializer.ParamErr("路径无效", nil) + return serializer.ParamErr("Invalid path", nil) } // 创建文件系统 fs, err := filesystem.NewFileSystem(share.Creator()) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() @@ -258,7 +258,7 @@ func (service *Service) List(c *gin.Context) serializer.Response { // 获取子项目 objects, err := fs.List(ctx, service.Path, nil) if err != nil { - return serializer.Err(serializer.CodeCreateFolderFailed, err.Error(), err) + return serializer.Err(serializer.CodeNotSet, err.Error(), err) } return serializer.Response{ @@ -273,13 +273,13 @@ func (service *Service) Thumb(c *gin.Context) serializer.Response { share := shareCtx.(*model.Share) if !share.IsDir { - return serializer.ParamErr("此分享无缩略图", nil) + return serializer.ParamErr("This share has no thumb", nil) } // 创建文件系统 fs, err := filesystem.NewFileSystem(share.Creator()) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() @@ -289,7 +289,7 @@ func (service *Service) Thumb(c *gin.Context) serializer.Response { // 找到缩略图的父目录 exist, parent := fs.IsPathExist(service.Path) if !exist { - return serializer.Err(serializer.CodeNotFound, "路径不存在", nil) + return serializer.Err(serializer.CodeParentNotExist, "", nil) } ctx := context.WithValue(context.Background(), fsctx.LimitParentCtx, parent) @@ -297,13 +297,13 @@ func (service *Service) Thumb(c *gin.Context) serializer.Response { // 获取文件ID fileID, err := hashid.DecodeHashID(c.Param("file"), hashid.FileID) if err != nil { - return serializer.ParamErr("无法解析文件ID", err) + return serializer.Err(serializer.CodeNotFound, "", err) } // 获取缩略图 resp, err := fs.GetThumb(ctx, uint(fileID)) if err != nil { - return serializer.Err(serializer.CodeNotSet, "无法获取缩略图", err) + return serializer.Err(serializer.CodeNotSet, "Failed to get thumb", err) } if resp.Redirect { @@ -328,17 +328,17 @@ func (service *ArchiveService) Archive(c *gin.Context) serializer.Response { // 是否有权限 if !user.Group.OptionsSerialized.ArchiveDownload { - return serializer.Err(serializer.CodeNoPermissionErr, "您的用户组无权进行此操作", nil) + return serializer.Err(serializer.CodeGroupNotAllowed, "", nil) } if !share.IsDir { - return serializer.ParamErr("此分享无法进行打包", nil) + return serializer.ParamErr("This share cannot be batch downloaded", nil) } // 创建文件系统 fs, err := filesystem.NewFileSystem(user) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() @@ -348,7 +348,7 @@ func (service *ArchiveService) Archive(c *gin.Context) serializer.Response { // 找到要打包文件的父目录 exist, parent := fs.IsPathExist(service.Path) if !exist { - return serializer.Err(serializer.CodeNotFound, "路径不存在", nil) + return serializer.Err(serializer.CodeParentNotExist, "", nil) } // 限制操作范围为父目录下 @@ -388,7 +388,7 @@ func (service *SearchService) Search(c *gin.Context) serializer.Response { // 创建文件系统 fs, err := filesystem.NewFileSystem(share.Creator()) if err != nil { - return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err) + return serializer.Err(serializer.CodeCreateFSError, "", err) } defer fs.Recycle() From 846438e3af4075092e05ceff852381dd5bd022c3 Mon Sep 17 00:00:00 2001 From: WeidiDeng Date: Mon, 22 Aug 2022 19:49:19 +0800 Subject: [PATCH 07/16] =?UTF-8?q?graceful=20=E5=85=B3=E9=97=AD=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8=20(#1416)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bootstrap/init.go | 11 +-------- go.mod | 4 +--- go.sum | 2 -- main.go | 59 +++++++++++++++++++++++++++++++++++++++-------- pkg/conf/conf.go | 3 ++- 5 files changed, 54 insertions(+), 25 deletions(-) diff --git a/bootstrap/init.go b/bootstrap/init.go index 2aea14d..0f93399 100644 --- a/bootstrap/init.go +++ b/bootstrap/init.go @@ -98,16 +98,7 @@ func Init(path string, statics fs.FS) { } for _, dependency := range dependencies { - switch dependency.mode { - case "master": - if conf.SystemConfig.Mode == "master" { - dependency.factory() - } - case "slave": - if conf.SystemConfig.Mode == "slave" { - dependency.factory() - } - default: + if dependency.mode == conf.SystemConfig.Mode || dependency.mode == "both" { dependency.factory() } } diff --git a/go.mod b/go.mod index 6cb1dc3..394b77a 100644 --- a/go.mod +++ b/go.mod @@ -38,7 +38,7 @@ require ( github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20200120023323-87ff3bc489ac github.com/upyun/go-sdk v2.1.0+incompatible golang.org/x/image v0.0.0-20211028202545-6944b10bf410 - gopkg.in/go-playground/validator.v9 v9.29.1 + golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba ) require ( @@ -99,7 +99,6 @@ require ( github.com/mattn/go-colorable v0.1.4 // indirect github.com/mattn/go-isatty v0.0.12 // indirect github.com/mattn/go-runewidth v0.0.12 // indirect - github.com/mattn/go-sqlite3 v1.14.7 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mitchellh/mapstructure v1.1.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -149,7 +148,6 @@ require ( golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect golang.org/x/sys v0.0.0-20211020174200-9d6173849985 // indirect golang.org/x/text v0.3.7 // indirect - golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect golang.org/x/tools v0.1.0 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index a29f4ce..23fd3af 100644 --- a/go.sum +++ b/go.sum @@ -1393,10 +1393,8 @@ gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qS gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= -gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= -gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc= gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/main.go b/main.go index 69e628b..b691f6e 100644 --- a/main.go +++ b/main.go @@ -1,14 +1,21 @@ package main import ( + "context" _ "embed" "flag" "io" "io/fs" + "net" + "net/http" "os" + "os/signal" "strings" + "syscall" + "time" "github.com/cloudreve/Cloudreve/v3/bootstrap" + model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/conf" "github.com/cloudreve/Cloudreve/v3/pkg/util" "github.com/cloudreve/Cloudreve/v3/routers" @@ -41,6 +48,9 @@ func init() { } func main() { + // 关闭数据库连接 + defer model.DB.Close() + if isEject { // 开始导出内置静态资源文件 bootstrap.Eject(staticFS) @@ -54,16 +64,35 @@ func main() { } api := routers.InitRouter() + server := &http.Server{Handler: api} + + // 收到信号后关闭服务器 + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT) + go func() { + sig := <-sigChan + util.Log().Info("收到信号 %s,开始关闭 server", sig) + ctx := context.Background() + if conf.SystemConfig.GracePeriod != 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, time.Duration(conf.SystemConfig.GracePeriod)*time.Second) + defer cancel() + } + + err := server.Shutdown(ctx) + if err != nil { + util.Log().Error("关闭 server 错误, %s", err) + } + }() // 如果启用了SSL if conf.SSLConfig.CertPath != "" { - go func() { - util.Log().Info("开始监听 %s", conf.SSLConfig.Listen) - if err := api.RunTLS(conf.SSLConfig.Listen, - conf.SSLConfig.CertPath, conf.SSLConfig.KeyPath); err != nil { - util.Log().Error("无法监听[%s],%s", conf.SSLConfig.Listen, err) - } - }() + util.Log().Info("开始监听 %s", conf.SSLConfig.Listen) + server.Addr = conf.SSLConfig.Listen + if err := server.ListenAndServeTLS(conf.SSLConfig.CertPath, conf.SSLConfig.KeyPath); err != nil { + util.Log().Error("无法监听[%s],%s", conf.SSLConfig.Listen, err) + return + } } // 如果启用了Unix @@ -78,14 +107,26 @@ func main() { api.TrustedPlatform = conf.UnixConfig.ProxyHeader util.Log().Info("开始监听 %s", conf.UnixConfig.Listen) - if err := api.RunUnix(conf.UnixConfig.Listen); err != nil { + if err := RunUnix(server); err != nil { util.Log().Error("无法监听[%s],%s", conf.UnixConfig.Listen, err) } return } util.Log().Info("开始监听 %s", conf.SystemConfig.Listen) - if err := api.Run(conf.SystemConfig.Listen); err != nil { + server.Addr = conf.SystemConfig.Listen + if err := server.ListenAndServe(); err != nil { util.Log().Error("无法监听[%s],%s", conf.SystemConfig.Listen, err) } } + +func RunUnix(server *http.Server) error { + listener, err := net.Listen("unix", conf.UnixConfig.Listen) + if err != nil { + return err + } + defer listener.Close() + defer os.Remove(conf.UnixConfig.Listen) + + return server.Serve(listener) +} diff --git a/pkg/conf/conf.go b/pkg/conf/conf.go index a9f7d06..5770e71 100644 --- a/pkg/conf/conf.go +++ b/pkg/conf/conf.go @@ -3,7 +3,7 @@ package conf import ( "github.com/cloudreve/Cloudreve/v3/pkg/util" "github.com/go-ini/ini" - "gopkg.in/go-playground/validator.v9" + "github.com/go-playground/validator/v10" ) // database 数据库 @@ -26,6 +26,7 @@ type system struct { Debug bool SessionSecret string HashIDSalt string + GracePeriod int `validate:"gte=0"` } type ssl struct { From b1685d2863d84658be3e142b5a9952eb149ee246 Mon Sep 17 00:00:00 2001 From: XYenon Date: Thu, 29 Sep 2022 09:24:58 +0800 Subject: [PATCH 08/16] feat: seeding status for aria2 download tasks (#1422) * feat: add aria2 seeding * fix: move RecycleTaskType to the bottom * refactor: refactor recycle aria2 temp file --- assets | 2 +- pkg/aria2/aria2.go | 6 +- pkg/aria2/common/common.go | 9 +- pkg/aria2/common/common_test.go | 27 ++-- pkg/aria2/monitor/monitor.go | 48 ++++++- pkg/aria2/monitor/monitor_test.go | 11 +- pkg/aria2/rpc/resp.go | 60 ++++---- .../driver/shadow/slaveinmaster/handler.go | 7 +- pkg/serializer/slave.go | 1 + pkg/serializer/slave_test.go | 14 +- pkg/task/job.go | 4 + pkg/task/job_test.go | 14 +- pkg/task/recycle.go | 130 ++++++++++++++++++ pkg/task/recycle_test.go | 117 ++++++++++++++++ pkg/task/slavetask/transfer.go | 13 +- pkg/task/tranfer.go | 13 -- service/aria2/manage.go | 4 +- service/explorer/slave.go | 7 +- service/user/register.go | 5 +- 19 files changed, 404 insertions(+), 88 deletions(-) create mode 100644 pkg/task/recycle.go create mode 100644 pkg/task/recycle_test.go diff --git a/assets b/assets index a1028e7..02d9320 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit a1028e7e0ae96be4bb67d8c117cf39e07c207473 +Subproject commit 02d93206cc5b943c34b5f5ac86c23dd96f5ef603 diff --git a/pkg/aria2/aria2.go b/pkg/aria2/aria2.go index 60d254e..f91766f 100644 --- a/pkg/aria2/aria2.go +++ b/pkg/aria2/aria2.go @@ -3,8 +3,6 @@ package aria2 import ( "context" "fmt" - "github.com/cloudreve/Cloudreve/v3/pkg/cluster" - "github.com/cloudreve/Cloudreve/v3/pkg/mq" "net/url" "sync" "time" @@ -14,6 +12,8 @@ import ( "github.com/cloudreve/Cloudreve/v3/pkg/aria2/monitor" "github.com/cloudreve/Cloudreve/v3/pkg/aria2/rpc" "github.com/cloudreve/Cloudreve/v3/pkg/balancer" + "github.com/cloudreve/Cloudreve/v3/pkg/cluster" + "github.com/cloudreve/Cloudreve/v3/pkg/mq" ) // Instance 默认使用的Aria2处理实例 @@ -40,7 +40,7 @@ func Init(isReload bool, pool cluster.Pool, mqClient mq.MQ) { if !isReload { // 从数据库中读取未完成任务,创建监控 - unfinished := model.GetDownloadsByStatus(common.Ready, common.Paused, common.Downloading) + unfinished := model.GetDownloadsByStatus(common.Ready, common.Paused, common.Downloading, common.Seeding) for i := 0; i < len(unfinished); i++ { // 创建任务监控 diff --git a/pkg/aria2/common/common.go b/pkg/aria2/common/common.go index 8f281d8..455c89f 100644 --- a/pkg/aria2/common/common.go +++ b/pkg/aria2/common/common.go @@ -46,6 +46,8 @@ const ( Canceled // Unknown 未知状态 Unknown + // Seeding 做种中 + Seeding ) var ( @@ -94,11 +96,14 @@ func (instance *DummyAria2) DeleteTempFile(src *model.Download) error { } // GetStatus 将给定的状态字符串转换为状态标识数字 -func GetStatus(status string) int { - switch status { +func GetStatus(status rpc.StatusInfo) int { + switch status.Status { case "complete": return Complete case "active": + if status.BitTorrent.Mode != "" && status.CompletedLength == status.TotalLength { + return Seeding + } return Downloading case "waiting": return Ready diff --git a/pkg/aria2/common/common_test.go b/pkg/aria2/common/common_test.go index a93f5f8..7b0f237 100644 --- a/pkg/aria2/common/common_test.go +++ b/pkg/aria2/common/common_test.go @@ -1,9 +1,11 @@ package common import ( - model "github.com/cloudreve/Cloudreve/v3/models" - "github.com/stretchr/testify/assert" "testing" + + model "github.com/cloudreve/Cloudreve/v3/models" + "github.com/cloudreve/Cloudreve/v3/pkg/aria2/rpc" + "github.com/stretchr/testify/assert" ) func TestDummyAria2(t *testing.T) { @@ -35,11 +37,18 @@ func TestDummyAria2(t *testing.T) { func TestGetStatus(t *testing.T) { a := assert.New(t) - a.Equal(GetStatus("complete"), Complete) - a.Equal(GetStatus("active"), Downloading) - a.Equal(GetStatus("waiting"), Ready) - a.Equal(GetStatus("paused"), Paused) - a.Equal(GetStatus("error"), Error) - a.Equal(GetStatus("removed"), Canceled) - a.Equal(GetStatus("unknown"), Unknown) + a.Equal(GetStatus(rpc.StatusInfo{Status: "complete"}), Complete) + a.Equal(GetStatus(rpc.StatusInfo{Status: "active", + BitTorrent: rpc.BitTorrentInfo{Mode: ""}}), Downloading) + a.Equal(GetStatus(rpc.StatusInfo{Status: "active", + BitTorrent: rpc.BitTorrentInfo{Mode: "single"}, + TotalLength: "100", CompletedLength: "50"}), Downloading) + a.Equal(GetStatus(rpc.StatusInfo{Status: "active", + BitTorrent: rpc.BitTorrentInfo{Mode: "multi"}, + TotalLength: "100", CompletedLength: "100"}), Seeding) + a.Equal(GetStatus(rpc.StatusInfo{Status: "waiting"}), Ready) + a.Equal(GetStatus(rpc.StatusInfo{Status: "paused"}), Paused) + a.Equal(GetStatus(rpc.StatusInfo{Status: "error"}), Error) + a.Equal(GetStatus(rpc.StatusInfo{Status: "removed"}), Canceled) + a.Equal(GetStatus(rpc.StatusInfo{Status: "unknown"}), Unknown) } diff --git a/pkg/aria2/monitor/monitor.go b/pkg/aria2/monitor/monitor.go index a515b66..531d6ed 100644 --- a/pkg/aria2/monitor/monitor.go +++ b/pkg/aria2/monitor/monitor.go @@ -109,14 +109,14 @@ func (monitor *Monitor) Update() bool { util.Log().Debug("离线下载[%s]更新状态[%s]", status.Gid, status.Status) - switch status.Status { - case "complete": + switch common.GetStatus(status) { + case common.Complete, common.Seeding: return monitor.Complete(task.TaskPoll) - case "error": + case common.Error: return monitor.Error(status) - case "active", "waiting", "paused": + case common.Downloading, common.Ready, common.Paused: return false - case "removed": + case common.Canceled: monitor.Task.Status = common.Canceled monitor.Task.Save() monitor.RemoveTempFolder() @@ -132,7 +132,7 @@ func (monitor *Monitor) UpdateTaskInfo(status rpc.StatusInfo) error { originSize := monitor.Task.TotalSize monitor.Task.GID = status.Gid - monitor.Task.Status = common.GetStatus(status.Status) + monitor.Task.Status = common.GetStatus(status) // 文件大小、已下载大小 total, err := strconv.ParseUint(status.TotalLength, 10, 64) @@ -235,6 +235,40 @@ func (monitor *Monitor) RemoveTempFolder() { // Complete 完成下载,返回是否中断监控 func (monitor *Monitor) Complete(pool task.Pool) bool { + // 未开始转存,提交转存任务 + if monitor.Task.TaskID == 0 { + return monitor.transfer(pool) + } + + // 做种完成 + if common.GetStatus(monitor.Task.StatusInfo) == common.Complete { + transferTask, err := model.GetTasksByID(monitor.Task.TaskID) + if err != nil { + monitor.setErrorStatus(err) + monitor.RemoveTempFolder() + return true + } + + // 转存完成,回收下载目录 + if transferTask.Type == task.TransferTaskType && transferTask.Status >= task.Error { + job, err := task.NewRecycleTask(monitor.Task) + if err != nil { + monitor.setErrorStatus(err) + monitor.RemoveTempFolder() + return true + } + + // 提交回收任务 + pool.Submit(job) + + return true + } + } + + return false +} + +func (monitor *Monitor) transfer(pool task.Pool) bool { // 创建中转任务 file := make([]string, 0, len(monitor.Task.StatusInfo.Files)) sizes := make(map[string]uint64, len(monitor.Task.StatusInfo.Files)) @@ -269,7 +303,7 @@ func (monitor *Monitor) Complete(pool task.Pool) bool { monitor.Task.TaskID = job.Model().ID monitor.Task.Save() - return true + return false } func (monitor *Monitor) setErrorStatus(err error) { diff --git a/pkg/aria2/monitor/monitor_test.go b/pkg/aria2/monitor/monitor_test.go index 885484a..a6be586 100644 --- a/pkg/aria2/monitor/monitor_test.go +++ b/pkg/aria2/monitor/monitor_test.go @@ -3,6 +3,8 @@ package monitor import ( "database/sql" "errors" + "testing" + "github.com/DATA-DOG/go-sqlmock" model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/aria2/common" @@ -13,7 +15,6 @@ import ( "github.com/jinzhu/gorm" "github.com/stretchr/testify/assert" testMock "github.com/stretchr/testify/mock" - "testing" ) var mock sqlmock.Sqlmock @@ -431,6 +432,14 @@ func TestMonitor_Complete(t *testing.T) { mock.ExpectExec("UPDATE(.+)downloads").WillReturnResult(sqlmock.NewResult(1, 1)) mock.ExpectCommit() + mock.ExpectQuery("SELECT(.+)tasks").WillReturnRows(sqlmock.NewRows([]string{"id", "type", "status"}).AddRow(1, 2, 4)) + mock.ExpectQuery("SELECT(.+)users").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(9414)) + mock.ExpectBegin() + mock.ExpectExec("INSERT(.+)tasks").WillReturnResult(sqlmock.NewResult(2, 1)) + mock.ExpectCommit() + + a.False(m.Complete(mockPool)) + m.Task.StatusInfo.Status = "complete" a.True(m.Complete(mockPool)) a.NoError(mock.ExpectationsWereMet()) mockNode.AssertExpectations(t) diff --git a/pkg/aria2/rpc/resp.go b/pkg/aria2/rpc/resp.go index e685ce6..3614228 100644 --- a/pkg/aria2/rpc/resp.go +++ b/pkg/aria2/rpc/resp.go @@ -4,35 +4,27 @@ package rpc // StatusInfo represents response of aria2.tellStatus type StatusInfo struct { - Gid string `json:"gid"` // GID of the download. - Status string `json:"status"` // active for currently downloading/seeding downloads. waiting for downloads in the queue; download is not started. paused for paused downloads. error for downloads that were stopped because of error. complete for stopped and completed downloads. removed for the downloads removed by user. - TotalLength string `json:"totalLength"` // Total length of the download in bytes. - CompletedLength string `json:"completedLength"` // Completed length of the download in bytes. - UploadLength string `json:"uploadLength"` // Uploaded length of the download in bytes. - BitField string `json:"bitfield"` // Hexadecimal representation of the download progress. The highest bit corresponds to the piece at index 0. Any set bits indicate loaded pieces, while unset bits indicate not yet loaded and/or missing pieces. Any overflow bits at the end are set to zero. When the download was not started yet, this key will not be included in the response. - DownloadSpeed string `json:"downloadSpeed"` // Download speed of this download measured in bytes/sec. - UploadSpeed string `json:"uploadSpeed"` // LocalUpload speed of this download measured in bytes/sec. - InfoHash string `json:"infoHash"` // InfoHash. BitTorrent only. - NumSeeders string `json:"numSeeders"` // The number of seeders aria2 has connected to. BitTorrent only. - Seeder string `json:"seeder"` // true if the local endpoint is a seeder. Otherwise false. BitTorrent only. - PieceLength string `json:"pieceLength"` // Piece length in bytes. - NumPieces string `json:"numPieces"` // The number of pieces. - Connections string `json:"connections"` // The number of peers/servers aria2 has connected to. - ErrorCode string `json:"errorCode"` // The code of the last error for this item, if any. The value is a string. The error codes are defined in the EXIT STATUS section. This value is only available for stopped/completed downloads. - ErrorMessage string `json:"errorMessage"` // The (hopefully) human readable error message associated to errorCode. - FollowedBy []string `json:"followedBy"` // List of GIDs which are generated as the result of this download. For example, when aria2 downloads a Metalink file, it generates downloads described in the Metalink (see the --follow-metalink option). This value is useful to track auto-generated downloads. If there are no such downloads, this key will not be included in the response. - BelongsTo string `json:"belongsTo"` // GID of a parent download. Some downloads are a part of another download. For example, if a file in a Metalink has BitTorrent resources, the downloads of ".torrent" files are parts of that parent. If this download has no parent, this key will not be included in the response. - Dir string `json:"dir"` // Directory to save files. - Files []FileInfo `json:"files"` // Returns the list of files. The elements of this list are the same structs used in aria2.getFiles() method. - BitTorrent struct { - AnnounceList [][]string `json:"announceList"` // List of lists of announce URIs. If the torrent contains announce and no announce-list, announce is converted to the announce-list format. - Comment string `json:"comment"` // The comment of the torrent. comment.utf-8 is used if available. - CreationDate int64 `json:"creationDate"` // The creation time of the torrent. The value is an integer since the epoch, measured in seconds. - Mode string `json:"mode"` // File mode of the torrent. The value is either single or multi. - Info struct { - Name string `json:"name"` // name in info dictionary. name.utf-8 is used if available. - } `json:"info"` // Struct which contains data from Info dictionary. It contains following keys. - } `json:"bittorrent"` // Struct which contains information retrieved from the .torrent (file). BitTorrent only. It contains following keys. + Gid string `json:"gid"` // GID of the download. + Status string `json:"status"` // active for currently downloading/seeding downloads. waiting for downloads in the queue; download is not started. paused for paused downloads. error for downloads that were stopped because of error. complete for stopped and completed downloads. removed for the downloads removed by user. + TotalLength string `json:"totalLength"` // Total length of the download in bytes. + CompletedLength string `json:"completedLength"` // Completed length of the download in bytes. + UploadLength string `json:"uploadLength"` // Uploaded length of the download in bytes. + BitField string `json:"bitfield"` // Hexadecimal representation of the download progress. The highest bit corresponds to the piece at index 0. Any set bits indicate loaded pieces, while unset bits indicate not yet loaded and/or missing pieces. Any overflow bits at the end are set to zero. When the download was not started yet, this key will not be included in the response. + DownloadSpeed string `json:"downloadSpeed"` // Download speed of this download measured in bytes/sec. + UploadSpeed string `json:"uploadSpeed"` // LocalUpload speed of this download measured in bytes/sec. + InfoHash string `json:"infoHash"` // InfoHash. BitTorrent only. + NumSeeders string `json:"numSeeders"` // The number of seeders aria2 has connected to. BitTorrent only. + Seeder string `json:"seeder"` // true if the local endpoint is a seeder. Otherwise false. BitTorrent only. + PieceLength string `json:"pieceLength"` // Piece length in bytes. + NumPieces string `json:"numPieces"` // The number of pieces. + Connections string `json:"connections"` // The number of peers/servers aria2 has connected to. + ErrorCode string `json:"errorCode"` // The code of the last error for this item, if any. The value is a string. The error codes are defined in the EXIT STATUS section. This value is only available for stopped/completed downloads. + ErrorMessage string `json:"errorMessage"` // The (hopefully) human readable error message associated to errorCode. + FollowedBy []string `json:"followedBy"` // List of GIDs which are generated as the result of this download. For example, when aria2 downloads a Metalink file, it generates downloads described in the Metalink (see the --follow-metalink option). This value is useful to track auto-generated downloads. If there are no such downloads, this key will not be included in the response. + BelongsTo string `json:"belongsTo"` // GID of a parent download. Some downloads are a part of another download. For example, if a file in a Metalink has BitTorrent resources, the downloads of ".torrent" files are parts of that parent. If this download has no parent, this key will not be included in the response. + Dir string `json:"dir"` // Directory to save files. + Files []FileInfo `json:"files"` // Returns the list of files. The elements of this list are the same structs used in aria2.getFiles() method. + BitTorrent BitTorrentInfo `json:"bittorrent"` // Struct which contains information retrieved from the .torrent (file). BitTorrent only. It contains following keys. } // URIInfo represents an element of response of aria2.getUris @@ -100,3 +92,13 @@ type Method struct { Name string `json:"methodName"` // Method name to call Params []interface{} `json:"params"` // Array containing parameters to the method call } + +type BitTorrentInfo struct { + AnnounceList [][]string `json:"announceList"` // List of lists of announce URIs. If the torrent contains announce and no announce-list, announce is converted to the announce-list format. + Comment string `json:"comment"` // The comment of the torrent. comment.utf-8 is used if available. + CreationDate int64 `json:"creationDate"` // The creation time of the torrent. The value is an integer since the epoch, measured in seconds. + Mode string `json:"mode"` // File mode of the torrent. The value is either single or multi. + Info struct { + Name string `json:"name"` // name in info dictionary. name.utf-8 is used if available. + } `json:"info"` // Struct which contains data from Info dictionary. It contains following keys. +} diff --git a/pkg/filesystem/driver/shadow/slaveinmaster/handler.go b/pkg/filesystem/driver/shadow/slaveinmaster/handler.go index 7fc7b09..4dd9da8 100644 --- a/pkg/filesystem/driver/shadow/slaveinmaster/handler.go +++ b/pkg/filesystem/driver/shadow/slaveinmaster/handler.go @@ -5,6 +5,9 @@ import ( "context" "encoding/json" "errors" + "net/url" + "time" + model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/cluster" "github.com/cloudreve/Cloudreve/v3/pkg/filesystem/driver" @@ -13,8 +16,6 @@ import ( "github.com/cloudreve/Cloudreve/v3/pkg/mq" "github.com/cloudreve/Cloudreve/v3/pkg/request" "github.com/cloudreve/Cloudreve/v3/pkg/serializer" - "net/url" - "time" ) // Driver 影子存储策略,将上传任务指派给从机节点处理,并等待从机通知上传结果 @@ -118,6 +119,6 @@ func (d *Driver) List(ctx context.Context, path string, recursive bool) ([]respo } // 取消上传凭证 -func (handler Driver) CancelToken(ctx context.Context, uploadSession *serializer.UploadSession) error { +func (d *Driver) CancelToken(ctx context.Context, uploadSession *serializer.UploadSession) error { return nil } diff --git a/pkg/serializer/slave.go b/pkg/serializer/slave.go index 245767a..04d56d3 100644 --- a/pkg/serializer/slave.go +++ b/pkg/serializer/slave.go @@ -4,6 +4,7 @@ import ( "crypto/sha1" "encoding/gob" "fmt" + model "github.com/cloudreve/Cloudreve/v3/models" ) diff --git a/pkg/serializer/slave_test.go b/pkg/serializer/slave_test.go index 6471542..add3a63 100644 --- a/pkg/serializer/slave_test.go +++ b/pkg/serializer/slave_test.go @@ -1,9 +1,10 @@ package serializer import ( + "testing" + model "github.com/cloudreve/Cloudreve/v3/models" "github.com/stretchr/testify/assert" - "testing" ) func TestSlaveTransferReq_Hash(t *testing.T) { @@ -18,3 +19,14 @@ func TestSlaveTransferReq_Hash(t *testing.T) { } a.NotEqual(s1.Hash("1"), s2.Hash("1")) } + +func TestSlaveRecycleReq_Hash(t *testing.T) { + a := assert.New(t) + s1 := &SlaveRecycleReq{ + Path: "1", + } + s2 := &SlaveRecycleReq{ + Path: "2", + } + a.NotEqual(s1.Hash("1"), s2.Hash("1")) +} diff --git a/pkg/task/job.go b/pkg/task/job.go index 781c460..e9d54d8 100644 --- a/pkg/task/job.go +++ b/pkg/task/job.go @@ -15,6 +15,8 @@ const ( TransferTaskType // ImportTaskType 导入任务 ImportTaskType + // RecycleTaskType 回收任务 + RecycleTaskType ) // 任务状态 @@ -113,6 +115,8 @@ func GetJobFromModel(task *model.Task) (Job, error) { return NewTransferTaskFromModel(task) case ImportTaskType: return NewImportTaskFromModel(task) + case RecycleTaskType: + return NewRecycleTaskFromModel(task) default: return nil, ErrUnknownTaskType } diff --git a/pkg/task/job_test.go b/pkg/task/job_test.go index 81793ee..737f5b7 100644 --- a/pkg/task/job_test.go +++ b/pkg/task/job_test.go @@ -2,12 +2,12 @@ package task import ( "errors" - testMock "github.com/stretchr/testify/mock" "testing" "github.com/DATA-DOG/go-sqlmock" model "github.com/cloudreve/Cloudreve/v3/models" "github.com/stretchr/testify/assert" + testMock "github.com/stretchr/testify/mock" ) func TestRecord(t *testing.T) { @@ -103,4 +103,16 @@ func TestGetJobFromModel(t *testing.T) { asserts.Nil(job) asserts.Error(err) } + // RecycleTaskType + { + task := &model.Task{ + Status: 0, + Type: RecycleTaskType, + } + mock.ExpectQuery("SELECT(.+)users(.+)").WillReturnError(errors.New("error")) + job, err := GetJobFromModel(task) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.Nil(job) + asserts.Error(err) + } } diff --git a/pkg/task/recycle.go b/pkg/task/recycle.go new file mode 100644 index 0000000..17eaf3c --- /dev/null +++ b/pkg/task/recycle.go @@ -0,0 +1,130 @@ +package task + +import ( + "encoding/json" + + model "github.com/cloudreve/Cloudreve/v3/models" + "github.com/cloudreve/Cloudreve/v3/pkg/cluster" + "github.com/cloudreve/Cloudreve/v3/pkg/util" +) + +// RecycleTask 文件回收任务 +type RecycleTask struct { + User *model.User + TaskModel *model.Task + TaskProps RecycleProps + Err *JobError +} + +// RecycleProps 回收任务属性 +type RecycleProps struct { + // 下载任务 GID + DownloadGID string `json:"download_gid"` +} + +// Props 获取任务属性 +func (job *RecycleTask) Props() string { + res, _ := json.Marshal(job.TaskProps) + return string(res) +} + +// Type 获取任务状态 +func (job *RecycleTask) Type() int { + return RecycleTaskType +} + +// Creator 获取创建者ID +func (job *RecycleTask) Creator() uint { + return job.User.ID +} + +// Model 获取任务的数据库模型 +func (job *RecycleTask) Model() *model.Task { + return job.TaskModel +} + +// SetStatus 设定状态 +func (job *RecycleTask) SetStatus(status int) { + job.TaskModel.SetStatus(status) +} + +// SetError 设定任务失败信息 +func (job *RecycleTask) SetError(err *JobError) { + job.Err = err + res, _ := json.Marshal(job.Err) + job.TaskModel.SetError(string(res)) +} + +// SetErrorMsg 设定任务失败信息 +func (job *RecycleTask) SetErrorMsg(msg string, err error) { + jobErr := &JobError{Msg: msg} + if err != nil { + jobErr.Error = err.Error() + } + job.SetError(jobErr) +} + +// GetError 返回任务失败信息 +func (job *RecycleTask) GetError() *JobError { + return job.Err +} + +// Do 开始执行任务 +func (job *RecycleTask) Do() { + download, err := model.GetDownloadByGid(job.TaskProps.DownloadGID, job.User.ID) + if err != nil { + util.Log().Warning("回收任务 %d 找不到下载记录", job.TaskModel.ID) + job.SetErrorMsg("无法找到下载任务", err) + return + } + nodeID := download.GetNodeID() + node := cluster.Default.GetNodeByID(nodeID) + if node == nil { + util.Log().Warning("回收任务 %d 找不到节点", job.TaskModel.ID) + job.SetErrorMsg("从机节点不可用", nil) + return + } + err = node.GetAria2Instance().DeleteTempFile(download) + if err != nil { + util.Log().Warning("无法删除中转临时目录[%s], %s", download.Parent, err) + job.SetErrorMsg("文件回收失败", err) + return + } +} + +// NewRecycleTask 新建回收任务 +func NewRecycleTask(download *model.Download) (Job, error) { + newTask := &RecycleTask{ + User: download.GetOwner(), + TaskProps: RecycleProps{ + DownloadGID: download.GID, + }, + } + + record, err := Record(newTask) + if err != nil { + return nil, err + } + newTask.TaskModel = record + + return newTask, nil +} + +// NewRecycleTaskFromModel 从数据库记录中恢复回收任务 +func NewRecycleTaskFromModel(task *model.Task) (Job, error) { + user, err := model.GetActiveUserByID(task.UserID) + if err != nil { + return nil, err + } + newTask := &RecycleTask{ + User: &user, + TaskModel: task, + } + + err = json.Unmarshal([]byte(task.Props), &newTask.TaskProps) + if err != nil { + return nil, err + } + + return newTask, nil +} diff --git a/pkg/task/recycle_test.go b/pkg/task/recycle_test.go new file mode 100644 index 0000000..0092a30 --- /dev/null +++ b/pkg/task/recycle_test.go @@ -0,0 +1,117 @@ +package task + +import ( + "errors" + "testing" + + "github.com/DATA-DOG/go-sqlmock" + model "github.com/cloudreve/Cloudreve/v3/models" + "github.com/jinzhu/gorm" + "github.com/stretchr/testify/assert" +) + +func TestRecycleTask_Props(t *testing.T) { + asserts := assert.New(t) + task := &RecycleTask{ + User: &model.User{}, + } + asserts.NotEmpty(task.Props()) + asserts.Equal(RecycleTaskType, task.Type()) + asserts.EqualValues(0, task.Creator()) + asserts.Nil(task.Model()) +} + +func TestRecycleTask_SetStatus(t *testing.T) { + asserts := assert.New(t) + task := &RecycleTask{ + User: &model.User{}, + TaskModel: &model.Task{ + Model: gorm.Model{ID: 1}, + }, + } + mock.ExpectBegin() + mock.ExpectExec("UPDATE(.+)").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectCommit() + task.SetStatus(3) + asserts.NoError(mock.ExpectationsWereMet()) +} + +func TestRecycleTask_SetError(t *testing.T) { + asserts := assert.New(t) + task := &RecycleTask{ + User: &model.User{}, + TaskModel: &model.Task{ + Model: gorm.Model{ID: 1}, + }, + } + + mock.ExpectBegin() + mock.ExpectExec("UPDATE(.+)").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectCommit() + + task.SetErrorMsg("error", nil) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.Equal("error", task.GetError().Msg) +} + +func TestNewRecycleTask(t *testing.T) { + asserts := assert.New(t) + + // 成功 + { + mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1)) + mock.ExpectBegin() + mock.ExpectExec("INSERT(.+)").WillReturnResult(sqlmock.NewResult(1, 1)) + mock.ExpectCommit() + job, err := NewRecycleTask(&model.Download{ + Model: gorm.Model{ID: 1}, + GID: "test_g_id", + Parent: "/", + UserID: 1, + NodeID: 1, + }) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.NotNil(job) + asserts.NoError(err) + } + + // 失败 + { + mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1)) + mock.ExpectBegin() + mock.ExpectExec("INSERT(.+)").WillReturnError(errors.New("error")) + mock.ExpectRollback() + job, err := NewRecycleTask(&model.Download{ + Model: gorm.Model{ID: 1}, + GID: "test_g_id", + Parent: "test/not_exist", + UserID: 1, + NodeID: 1, + }) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.Nil(job) + asserts.Error(err) + } +} + +func TestNewRecycleTaskFromModel(t *testing.T) { + asserts := assert.New(t) + + // 成功 + { + mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1)) + job, err := NewRecycleTaskFromModel(&model.Task{Props: "{}"}) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.NoError(err) + asserts.NotNil(job) + } + + // JSON解析失败 + { + mock.ExpectQuery("SELECT(.+)").WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1)) + job, err := NewRecycleTaskFromModel(&model.Task{Props: "?"}) + asserts.NoError(mock.ExpectationsWereMet()) + asserts.Error(err) + asserts.Nil(job) + } +} diff --git a/pkg/task/slavetask/transfer.go b/pkg/task/slavetask/transfer.go index 20c5fcc..818028e 100644 --- a/pkg/task/slavetask/transfer.go +++ b/pkg/task/slavetask/transfer.go @@ -2,6 +2,8 @@ package slavetask import ( "context" + "os" + model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/cluster" "github.com/cloudreve/Cloudreve/v3/pkg/filesystem" @@ -10,7 +12,6 @@ import ( "github.com/cloudreve/Cloudreve/v3/pkg/serializer" "github.com/cloudreve/Cloudreve/v3/pkg/task" "github.com/cloudreve/Cloudreve/v3/pkg/util" - "os" ) // TransferTask 文件中转任务 @@ -79,8 +80,6 @@ func (job *TransferTask) GetError() *task.JobError { // Do 开始执行任务 func (job *TransferTask) Do() { - defer job.Recycle() - fs, err := filesystem.NewAnonymousFileSystem() if err != nil { job.SetErrorMsg("无法初始化匿名文件系统", err) @@ -137,11 +136,3 @@ func (job *TransferTask) Do() { util.Log().Warning("无法发送转存成功通知到从机, %s", err) } } - -// Recycle 回收临时文件 -func (job *TransferTask) Recycle() { - err := os.Remove(job.Req.Src) - if err != nil { - util.Log().Warning("无法删除中转临时文件[%s], %s", job.Req.Src, err) - } -} diff --git a/pkg/task/tranfer.go b/pkg/task/tranfer.go index 5f9aa58..f115e80 100644 --- a/pkg/task/tranfer.go +++ b/pkg/task/tranfer.go @@ -3,7 +3,6 @@ package task import ( "context" "encoding/json" - "os" "path" "path/filepath" "strings" @@ -87,8 +86,6 @@ func (job *TransferTask) GetError() *JobError { // Do 开始执行任务 func (job *TransferTask) Do() { - defer job.Recycle() - // 创建文件系统 fs, err := filesystem.NewFileSystem(job.User) if err != nil { @@ -139,16 +136,6 @@ func (job *TransferTask) Do() { } -// Recycle 回收临时文件 -func (job *TransferTask) Recycle() { - if job.TaskProps.NodeID == 1 { - err := os.RemoveAll(job.TaskProps.Parent) - if err != nil { - util.Log().Warning("无法删除中转临时目录[%s], %s", job.TaskProps.Parent, err) - } - } -} - // NewTransferTask 新建中转任务 func NewTransferTask(user uint, src []string, dst, parent string, trim bool, node uint, sizes map[string]uint64) (Job, error) { creator, err := model.GetActiveUserByID(user) diff --git a/service/aria2/manage.go b/service/aria2/manage.go index 6344ddd..115a440 100644 --- a/service/aria2/manage.go +++ b/service/aria2/manage.go @@ -33,7 +33,7 @@ func (service *DownloadListService) Finished(c *gin.Context, user *model.User) s // Downloading 获取正在下载中的任务 func (service *DownloadListService) Downloading(c *gin.Context, user *model.User) serializer.Response { // 查找下载记录 - downloads := model.GetDownloadsByStatusAndUser(service.Page, user.ID, common.Downloading, common.Paused, common.Ready) + downloads := model.GetDownloadsByStatusAndUser(service.Page, user.ID, common.Downloading, common.Seeding, common.Paused, common.Ready) intervals := make(map[uint]int) for _, download := range downloads { if _, ok := intervals[download.ID]; !ok { @@ -57,7 +57,7 @@ func (service *DownloadTaskService) Delete(c *gin.Context) serializer.Response { return serializer.Err(serializer.CodeNotFound, "Download record not found", err) } - if download.Status >= common.Error { + if download.Status >= common.Error && download.Status <= common.Unknown { // 如果任务已完成,则删除任务记录 if err := download.Delete(); err != nil { return serializer.DBErr("Failed to delete task record", err) diff --git a/service/explorer/slave.go b/service/explorer/slave.go index 1435640..afb61af 100644 --- a/service/explorer/slave.go +++ b/service/explorer/slave.go @@ -5,6 +5,10 @@ import ( "encoding/base64" "encoding/json" "fmt" + "net/http" + "net/url" + "time" + model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/cache" "github.com/cloudreve/Cloudreve/v3/pkg/cluster" @@ -16,9 +20,6 @@ import ( "github.com/cloudreve/Cloudreve/v3/pkg/util" "github.com/gin-gonic/gin" "github.com/jinzhu/gorm" - "net/http" - "net/url" - "time" ) // SlaveDownloadService 从机文件下載服务 diff --git a/service/user/register.go b/service/user/register.go index d3c81b5..35e8253 100644 --- a/service/user/register.go +++ b/service/user/register.go @@ -1,14 +1,15 @@ package user import ( + "net/url" + "strings" + model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/auth" "github.com/cloudreve/Cloudreve/v3/pkg/email" "github.com/cloudreve/Cloudreve/v3/pkg/hashid" "github.com/cloudreve/Cloudreve/v3/pkg/serializer" "github.com/gin-gonic/gin" - "net/url" - "strings" ) // UserRegisterService 管理用户注册的服务 From 88e10aeaa28f1bd77396b79d00b5c27b399109c8 Mon Sep 17 00:00:00 2001 From: topjohncian <45138629+topjohncian@users.noreply.github.com> Date: Thu, 29 Sep 2022 09:26:30 +0800 Subject: [PATCH 09/16] Fix: unexpected querying all files when deleting an empty folder (#1469) --- models/file.go | 4 ++++ models/file_test.go | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/models/file.go b/models/file.go index 34dad78..8171054 100644 --- a/models/file.go +++ b/models/file.go @@ -186,6 +186,10 @@ func RemoveFilesWithSoftLinks(files []File) ([]File, error) { // 结果值 filteredFiles := make([]File, 0) + if len(files) == 0 { + return filteredFiles, nil + } + // 查询软链接的文件 var filesWithSoftLinks []File tx := DB diff --git a/models/file_test.go b/models/file_test.go index b421763..9563521 100644 --- a/models/file_test.go +++ b/models/file_test.go @@ -257,6 +257,19 @@ func TestFile_GetPolicy(t *testing.T) { } } +func TestRemoveFilesWithSoftLinks_EmptyArg(t *testing.T) { + asserts := assert.New(t) + // 传入空 + { + mock.ExpectQuery("SELECT(.+)files(.+)") + file, err := RemoveFilesWithSoftLinks([]File{}) + asserts.Error(mock.ExpectationsWereMet()) + asserts.NoError(err) + asserts.Equal(len(file), 0) + DB.Find(&File{}) + } +} + func TestRemoveFilesWithSoftLinks(t *testing.T) { asserts := assert.New(t) files := []File{ From 9464ee2103a2e8ef18ca63024904ca4a2b6f442a Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Thu, 29 Sep 2022 17:37:05 +0800 Subject: [PATCH 10/16] i18n: user route --- assets | 2 +- pkg/serializer/error.go | 14 ++++++++++++++ routers/controllers/user.go | 20 ++++++++++---------- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/assets b/assets index a1028e7..02d9320 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit a1028e7e0ae96be4bb67d8c117cf39e07c207473 +Subproject commit 02d93206cc5b943c34b5f5ac86c23dd96f5ef603 diff --git a/pkg/serializer/error.go b/pkg/serializer/error.go index 82c37e1..1b4aaf3 100644 --- a/pkg/serializer/error.go +++ b/pkg/serializer/error.go @@ -172,6 +172,20 @@ const ( CodeSlavePingMaster = 40060 // Cloudreve 版本不一致 CodeVersionMismatch = 40061 + // 积分不足 + CodeInsufficientCredit = 40062 + // 用户组冲突 + CodeGroupConflict = 40063 + // 当前已处于此用户组中 + CodeGroupInvalid = 40064 + // 兑换码无效 + CodeInvalidGiftCode = 40065 + // 已绑定了QQ账号 + CodeQQBindConflict = 40066 + // QQ账号已被绑定其他账号 + CodeQQBindOtherAccount = 40067 + // QQ 未绑定对应账号 + CodeQQNotLinked = 40068 // CodeDBError 数据库操作失败 CodeDBError = 50001 // CodeEncryptError 加密失败 diff --git a/routers/controllers/user.go b/routers/controllers/user.go index a99b6c8..7d1cc42 100644 --- a/routers/controllers/user.go +++ b/routers/controllers/user.go @@ -20,7 +20,7 @@ func StartLoginAuthn(c *gin.Context) { userName := c.Param("username") expectedUser, err := model.GetActiveUserByEmail(userName) if err != nil { - c.JSON(200, serializer.Err(serializer.CodeUserNotFound, "User not exist", err)) + c.JSON(200, serializer.Err(serializer.CodeUserNotFound, "", 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.CodeUserNotFound, "User not exist", err)) + c.JSON(200, serializer.Err(serializer.CodeUserNotFound, "", err)) return } @@ -88,7 +88,7 @@ func StartRegAuthn(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 } @@ -121,7 +121,7 @@ func FinishRegAuthn(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 } @@ -271,26 +271,26 @@ func UploadAvatar(c *gin.Context) { maxSize := model.GetIntSetting("avatar_size", 2097152) if c.Request.ContentLength == -1 || c.Request.ContentLength > int64(maxSize) { request.BlackHole(c.Request.Body) - c.JSON(200, serializer.Err(serializer.CodeUploadFailed, "头像尺寸太大", nil)) + c.JSON(200, serializer.Err(serializer.CodeFileTooLarge, "", nil)) return } // 取得上传的文件 file, err := c.FormFile("avatar") if err != nil { - c.JSON(200, serializer.Err(serializer.CodeIOFailed, "无法读取头像数据", err)) + c.JSON(200, serializer.ParamErr("Failed to read avatar file data", err)) return } // 初始化头像 r, err := file.Open() if err != nil { - c.JSON(200, serializer.Err(serializer.CodeIOFailed, "无法读取头像数据", err)) + c.JSON(200, serializer.ParamErr("Failed to read avatar file data", err)) return } avatar, err := thumb.NewThumbFromFile(r, file.Filename) if err != nil { - c.JSON(200, serializer.Err(serializer.CodeIOFailed, "无法解析图像数据", err)) + c.JSON(200, serializer.ParamErr("Invalid image", err)) return } @@ -298,7 +298,7 @@ func UploadAvatar(c *gin.Context) { u := CurrentUser(c) err = avatar.CreateAvatar(u.ID) if err != nil { - c.JSON(200, serializer.Err(serializer.CodeIOFailed, "无法创建头像", err)) + c.JSON(200, serializer.Err(serializer.CodeIOFailed, "Failed to create avatar file", err)) return } @@ -306,7 +306,7 @@ func UploadAvatar(c *gin.Context) { if err := u.Update(map[string]interface{}{ "avatar": "file", }); err != nil { - c.JSON(200, serializer.Err(serializer.CodeDBError, "无法更新头像", err)) + c.JSON(200, serializer.DBErr("Failed to update avatar attribute", err)) return } From 16d17ac1e61529fc4ad40010a3d727f39e21eac2 Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Thu, 29 Sep 2022 17:38:52 +0800 Subject: [PATCH 11/16] i18n: user setting route --- pkg/serializer/error.go | 2 ++ routers/controllers/webdav.go | 2 +- service/admin/file.go | 2 +- service/callback/oauth.go | 12 ++++++------ service/user/setting.go | 26 +++++++++++++------------- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/pkg/serializer/error.go b/pkg/serializer/error.go index 1b4aaf3..d586ac5 100644 --- a/pkg/serializer/error.go +++ b/pkg/serializer/error.go @@ -186,6 +186,8 @@ const ( CodeQQBindOtherAccount = 40067 // QQ 未绑定对应账号 CodeQQNotLinked = 40068 + // 密码不正确 + CodeIncorrectPassword = 40069 // CodeDBError 数据库操作失败 CodeDBError = 50001 // CodeEncryptError 加密失败 diff --git a/routers/controllers/webdav.go b/routers/controllers/webdav.go index 991bcf1..d44cd18 100644 --- a/routers/controllers/webdav.go +++ b/routers/controllers/webdav.go @@ -24,7 +24,7 @@ func init() { func ServeWebDAV(c *gin.Context) { fs, err := filesystem.NewFileSystemFromContext(c) if err != nil { - util.Log().Warning("无法为WebDAV初始化文件系统,%s", err) + util.Log().Warning("Failed to initialize filesystem for WebDAV,%s", err) return } diff --git a/service/admin/file.go b/service/admin/file.go index 1d7bc2b..84d8129 100644 --- a/service/admin/file.go +++ b/service/admin/file.go @@ -63,7 +63,7 @@ func (service *ListFolderService) List(c *gin.Context) serializer.Response { // 查找用户 user, err := model.GetUserByID(service.ID) if err != nil { - return serializer.Err(serializer.CodeUserNotFound, "用户不存在", err) + return serializer.Err(serializer.CodeUserNotFound, "", err) } // 创建文件系统 diff --git a/service/callback/oauth.go b/service/callback/oauth.go index 7aae847..2494982 100644 --- a/service/callback/oauth.go +++ b/service/callback/oauth.go @@ -28,36 +28,36 @@ func (service *OneDriveOauthService) Auth(c *gin.Context) serializer.Response { policyID, ok := util.GetSession(c, "onedrive_oauth_policy").(uint) if !ok { - return serializer.Err(serializer.CodeNotFound, "授权会话不存在,请重试", nil) + return serializer.Err(serializer.CodeNotFound, "", nil) } util.DeleteSession(c, "onedrive_oauth_policy") policy, err := model.GetPolicyByID(policyID) if err != nil { - return serializer.Err(serializer.CodeNotFound, "存储策略不存在", nil) + return serializer.Err(serializer.CodePolicyNotExist, "", nil) } client, err := onedrive.NewClient(&policy) if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "无法初始化 OneDrive 客户端", err) + return serializer.Err(serializer.CodeInternalSetting, "Failed to initialize OneDrive client", err) } credential, err := client.ObtainToken(c, onedrive.WithCode(service.Code)) if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "AccessToken 获取失败", err) + return serializer.Err(serializer.CodeInternalSetting, "Failed to fetch AccessToken", err) } // 更新存储策略的 RefreshToken client.Policy.AccessKey = credential.RefreshToken if err := client.Policy.SaveAndClearCache(); err != nil { - return serializer.DBErr("无法更新 RefreshToken", err) + return serializer.DBErr("Failed to update RefreshToken", err) } cache.Deletes([]string{client.Policy.AccessKey}, "onedrive_") if client.Policy.OptionsSerialized.OdDriver != "" && strings.Contains(client.Policy.OptionsSerialized.OdDriver, "http") { if err := querySharePointSiteID(c, client.Policy); err != nil { - return serializer.Err(serializer.CodeInternalSetting, "无法查询 SharePoint 站点 ID", err) + return serializer.Err(serializer.CodeInternalSetting, "Failed to query SharePoint site ID", err) } } diff --git a/service/user/setting.go b/service/user/setting.go index ad7f54b..8d7f619 100644 --- a/service/user/setting.go +++ b/service/user/setting.go @@ -80,7 +80,7 @@ type ThemeChose struct { func (service *ThemeChose) Update(c *gin.Context, user *model.User) serializer.Response { user.OptionsSerialized.PreferredTheme = service.Theme if err := user.UpdateOptions(); err != nil { - return serializer.DBErr("主题切换失败", err) + return serializer.DBErr("Failed to update user preferences", err) } return serializer.Response{} @@ -98,25 +98,25 @@ func (service *Enable2FA) Update(c *gin.Context, user *model.User) serializer.Re // 开启2FA secret, ok := util.GetSession(c, "2fa_init").(string) if !ok { - return serializer.Err(serializer.CodeParamErr, "未初始化二步验证", nil) + return serializer.Err(serializer.CodeInternalSetting, "You have not initiated 2FA session", nil) } if !totp.Validate(service.Code, secret) { - return serializer.ParamErr("验证码不正确", nil) + return serializer.ParamErr("Incorrect 2FA code", nil) } if err := user.Update(map[string]interface{}{"two_factor": secret}); err != nil { - return serializer.DBErr("无法更新二步验证设定", err) + return serializer.DBErr("Failed to update user preferences", err) } } else { // 关闭2FA if !totp.Validate(service.Code, user.TwoFactor) { - return serializer.ParamErr("验证码不正确", nil) + return serializer.ParamErr("Incorrect 2FA code", nil) } if err := user.Update(map[string]interface{}{"two_factor": ""}); err != nil { - return serializer.DBErr("无法更新二步验证设定", err) + return serializer.DBErr("Failed to update user preferences", err) } } @@ -130,7 +130,7 @@ func (service *SettingService) Init2FA(c *gin.Context, user *model.User) seriali AccountName: user.Email, }) if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "无法生成验密钥", err) + return serializer.Err(serializer.CodeInternalSetting, "Failed to generate TOTP secret", err) } util.SetSession(c, map[string]interface{}{"2fa_init": key.Secret()}) @@ -141,13 +141,13 @@ func (service *SettingService) Init2FA(c *gin.Context, user *model.User) seriali func (service *PasswordChange) Update(c *gin.Context, user *model.User) serializer.Response { // 验证老密码 if ok, _ := user.CheckPassword(service.Old); !ok { - return serializer.Err(serializer.CodeParamErr, "原密码不正确", nil) + return serializer.Err(serializer.CodeIncorrectPassword, "", nil) } // 更改为新密码 user.SetPassword(service.New) if err := user.Update(map[string]interface{}{"password": user.Password}); err != nil { - return serializer.DBErr("密码更换失败", err) + return serializer.DBErr("Failed to update password", err) } return serializer.Response{} @@ -157,7 +157,7 @@ func (service *PasswordChange) Update(c *gin.Context, user *model.User) serializ func (service *HomePage) Update(c *gin.Context, user *model.User) serializer.Response { user.OptionsSerialized.ProfileOff = !service.Enabled if err := user.UpdateOptions(); err != nil { - return serializer.DBErr("存储策略切换失败", err) + return serializer.DBErr("Failed to update user preferences", err) } return serializer.Response{} @@ -166,7 +166,7 @@ func (service *HomePage) Update(c *gin.Context, user *model.User) serializer.Res // Update 更改昵称 func (service *ChangerNick) Update(c *gin.Context, user *model.User) serializer.Response { if err := user.Update(map[string]interface{}{"nick": service.Nick}); err != nil { - return serializer.DBErr("无法更新昵称", err) + return serializer.DBErr("Failed to update user", err) } return serializer.Response{} @@ -178,7 +178,7 @@ func (service *AvatarService) Get(c *gin.Context) serializer.Response { uid, _ := c.Get("object_id") user, err := model.GetActiveUserByID(uid.(uint)) if err != nil { - return serializer.Err(serializer.CodeNotFound, "用户不存在", err) + return serializer.Err(serializer.CodeUserNotFound, "", err) } // 未设定头像时,返回404错误 @@ -199,7 +199,7 @@ func (service *AvatarService) Get(c *gin.Context) serializer.Response { server := model.GetSettingByName("gravatar_server") gravatarRoot, err := url.Parse(server) if err != nil { - return serializer.Err(serializer.CodeInternalSetting, "无法解析 Gravatar 服务器地址", err) + return serializer.Err(serializer.CodeInternalSetting, "Failed to parse Gravatar server", err) } email_lowered := strings.ToLower(user.Email) has := md5.Sum([]byte(email_lowered)) From db23f4061da2fd688e22641176bf46a977e0072d Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Thu, 29 Sep 2022 17:39:48 +0800 Subject: [PATCH 12/16] i18n: logs in bootstrapper and response code in middleware --- bootstrap/script.go | 4 ++-- bootstrap/static.go | 28 ++++++++++++++-------------- middleware/auth.go | 24 ++++++++++++------------ middleware/cluster.go | 8 ++++---- middleware/common.go | 2 +- middleware/frontend.go | 4 ++-- middleware/session.go | 6 +++--- middleware/share.go | 14 +++++++------- pkg/serializer/error.go | 2 ++ 9 files changed, 47 insertions(+), 45 deletions(-) diff --git a/bootstrap/script.go b/bootstrap/script.go index 7db59e8..6f0ac92 100644 --- a/bootstrap/script.go +++ b/bootstrap/script.go @@ -10,9 +10,9 @@ func RunScript(name string) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() if err := invoker.RunDBScript(name, ctx); err != nil { - util.Log().Error("数据库脚本执行失败: %s", err) + util.Log().Error("Failed to execute database script: %s", err) return } - util.Log().Info("数据库脚本 [%s] 执行完毕", name) + util.Log().Info("Finish executing database script %q.", name) } diff --git a/bootstrap/static.go b/bootstrap/static.go index 21d4a04..233e22a 100644 --- a/bootstrap/static.go +++ b/bootstrap/static.go @@ -46,13 +46,13 @@ func (b *GinFS) Exists(prefix string, filepath string) bool { // InitStatic 初始化静态资源文件 func InitStatic(statics fs.FS) { if util.Exists(util.RelativePath(StaticFolder)) { - util.Log().Info("检测到 statics 目录存在,将使用此目录下的静态资源文件") + util.Log().Info("Folder with name \"statics\" already exists, it will be used to serve static files.") StaticFS = static.LocalFile(util.RelativePath("statics"), false) } else { // 初始化静态资源 embedFS, err := fs.Sub(statics, "assets/build") if err != nil { - util.Log().Panic("无法初始化静态资源, %s", err) + util.Log().Panic("Failed to initialize static resources: %s", err) } StaticFS = &GinFS{ @@ -62,19 +62,19 @@ func InitStatic(statics fs.FS) { // 检查静态资源的版本 f, err := StaticFS.Open("version.json") if err != nil { - util.Log().Warning("静态资源版本标识文件不存在,请重新构建或删除 statics 目录") + util.Log().Warning("Missing version identifier file in static resources, please delete \"statics\" folder and rebuild it.") return } b, err := io.ReadAll(f) if err != nil { - util.Log().Warning("无法读取静态资源文件版本,请重新构建或删除 statics 目录") + util.Log().Warning("Failed to read version identifier file in static resources, please delete \"statics\" folder and rebuild it.") return } var v staticVersion if err := json.Unmarshal(b, &v); err != nil { - util.Log().Warning("无法解析静态资源文件版本, %s", err) + util.Log().Warning("Failed to parse version identifier file in static resources: %s", err) return } @@ -84,12 +84,12 @@ func InitStatic(statics fs.FS) { } if v.Name != staticName { - util.Log().Warning("静态资源版本不匹配,请重新构建或删除 statics 目录") + util.Log().Warning("Static resource version mismatch, please delete \"statics\" folder and rebuild it.") return } if v.Version != conf.RequiredStaticVersion { - util.Log().Warning("静态资源版本不匹配 [当前 %s, 需要: %s],请重新构建或删除 statics 目录", v.Version, conf.RequiredStaticVersion) + util.Log().Warning("Static resource version mismatch [Current %s, Desired: %s],please delete \"statics\" folder and rebuild it.", v.Version, conf.RequiredStaticVersion) return } } @@ -99,13 +99,13 @@ func Eject(statics fs.FS) { // 初始化静态资源 embedFS, err := fs.Sub(statics, "assets/build") if err != nil { - util.Log().Panic("无法初始化静态资源, %s", err) + util.Log().Panic("Failed to initialize static resources: %s", err) } var walk func(relPath string, d fs.DirEntry, err error) error walk = func(relPath string, d fs.DirEntry, err error) error { if err != nil { - return errors.Errorf("无法获取[%s]的信息, %s, 跳过...", relPath, err) + return errors.Errorf("Failed to read info of %q: %s, skipping...", relPath, err) } if !d.IsDir() { @@ -114,13 +114,13 @@ func Eject(statics fs.FS) { defer out.Close() if err != nil { - return errors.Errorf("无法创建文件[%s], %s, 跳过...", relPath, err) + return errors.Errorf("Failed to create file %q: %s, skipping...", relPath, err) } - util.Log().Info("导出 [%s]...", relPath) + util.Log().Info("Ejecting %q...", relPath) obj, _ := embedFS.Open(relPath) if _, err := io.Copy(out, bufio.NewReader(obj)); err != nil { - return errors.Errorf("无法写入文件[%s], %s, 跳过...", relPath, err) + return errors.Errorf("Cannot write file %q: %s, skipping...", relPath, err) } } return nil @@ -129,8 +129,8 @@ func Eject(statics fs.FS) { // util.Log().Info("开始导出内置静态资源...") err = fs.WalkDir(embedFS, ".", walk) if err != nil { - util.Log().Error("导出内置静态资源遇到错误:%s", err) + util.Log().Error("Error occurs while ejecting static resources: %s", err) return } - util.Log().Info("内置静态资源导出完成") + util.Log().Info("Finish ejecting static resources.") } diff --git a/middleware/auth.go b/middleware/auth.go index 7d1dd3f..67c898e 100644 --- a/middleware/auth.go +++ b/middleware/auth.go @@ -142,18 +142,18 @@ func uploadCallbackCheck(c *gin.Context, policyType string) serializer.Response // 验证 Callback Key sessionID := c.Param("sessionID") if sessionID == "" { - return serializer.ParamErr("Session ID 不能为空", nil) + return serializer.ParamErr("Session ID cannot be empty", nil) } callbackSessionRaw, exist := cache.Get(filesystem.UploadSessionCachePrefix + sessionID) if !exist { - return serializer.ParamErr("上传会话不存在或已过期", nil) + return serializer.Err(serializer.CodeUploadSessionExpired, "上传会话不存在或已过期", nil) } callbackSession := callbackSessionRaw.(serializer.UploadSession) c.Set(filesystem.UploadSessionCtx, &callbackSession) if callbackSession.Policy.Type != policyType { - return serializer.Err(serializer.CodePolicyNotAllowed, "Policy not supported", nil) + return serializer.Err(serializer.CodePolicyNotAllowed, "", nil) } // 清理回调会话 @@ -162,7 +162,7 @@ func uploadCallbackCheck(c *gin.Context, policyType string) serializer.Response // 查找用户 user, err := model.GetActiveUserByID(callbackSession.UID) if err != nil { - return serializer.Err(serializer.CodeCheckLogin, "找不到用户", err) + return serializer.Err(serializer.CodeUserNotFound, "", err) } c.Set(filesystem.UserCtx, &user) return serializer.Response{} @@ -194,14 +194,14 @@ func QiniuCallbackAuth() gin.HandlerFunc { mac := qbox.NewMac(session.Policy.AccessKey, session.Policy.SecretKey) ok, err := mac.VerifyCallback(c.Request) if err != nil { - util.Log().Debug("无法验证回调请求,%s", err) - c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "无法验证回调请求"}) + util.Log().Debug("Failed to verify callback request: %s", err) + c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "Failed to verify callback request."}) c.Abort() return } if !ok { - c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "回调签名无效"}) + c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "Invalid signature."}) c.Abort() return } @@ -215,8 +215,8 @@ func OSSCallbackAuth() gin.HandlerFunc { return func(c *gin.Context) { err := oss.VerifyCallbackSignature(c.Request) if err != nil { - util.Log().Debug("回调签名验证失败,%s", err) - c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "回调签名验证失败"}) + util.Log().Debug("Failed to verify callback request: %s", err) + c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "Failed to verify callback request."}) c.Abort() return } @@ -250,7 +250,7 @@ func UpyunCallbackAuth() gin.HandlerFunc { // 计算正文MD5 actualContentMD5 := fmt.Sprintf("%x", md5.Sum(body)) if actualContentMD5 != contentMD5 { - c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "MD5不一致"}) + c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "MD5 mismatch."}) c.Abort() return } @@ -265,7 +265,7 @@ func UpyunCallbackAuth() gin.HandlerFunc { // 对比签名 if signature != actualSignature { - c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "鉴权失败"}) + c.JSON(401, serializer.GeneralUploadCallbackFailed{Error: "Signature not match"}) c.Abort() return } @@ -289,7 +289,7 @@ func IsAdmin() gin.HandlerFunc { return func(c *gin.Context) { user, _ := c.Get("user") if user.(*model.User).Group.ID != 1 && user.(*model.User).ID != 1 { - c.JSON(200, serializer.Err(serializer.CodeAdminRequired, "您不是管理组成员", nil)) + c.JSON(200, serializer.Err(serializer.CodeNoPermissionErr, "", nil)) c.Abort() return } diff --git a/middleware/cluster.go b/middleware/cluster.go index fdcb7f8..2e814be 100644 --- a/middleware/cluster.go +++ b/middleware/cluster.go @@ -25,7 +25,7 @@ func UseSlaveAria2Instance(clusterController cluster.Controller) gin.HandlerFunc // 获取对应主机节点的从机Aria2实例 caller, err := clusterController.GetAria2Instance(siteID.(string)) if err != nil { - c.JSON(200, serializer.Err(serializer.CodeNotSet, "无法获取 Aria2 实例", err)) + c.JSON(200, serializer.Err(serializer.CodeNotSet, "Failed to get Aria2 instance", err)) c.Abort() return } @@ -35,7 +35,7 @@ func UseSlaveAria2Instance(clusterController cluster.Controller) gin.HandlerFunc return } - c.JSON(200, serializer.ParamErr("未知的主机节点ID", nil)) + c.JSON(200, serializer.ParamErr("Unknown master node ID", nil)) c.Abort() } } @@ -44,14 +44,14 @@ func SlaveRPCSignRequired(nodePool cluster.Pool) gin.HandlerFunc { return func(c *gin.Context) { nodeID, err := strconv.ParseUint(c.GetHeader(auth.CrHeaderPrefix+"Node-Id"), 10, 64) if err != nil { - c.JSON(200, serializer.ParamErr("未知的主机节点ID", err)) + c.JSON(200, serializer.ParamErr("Unknown master node ID", err)) c.Abort() return } slaveNode := nodePool.GetNodeByID(uint(nodeID)) if slaveNode == nil { - c.JSON(200, serializer.ParamErr("未知的主机节点ID", err)) + c.JSON(200, serializer.ParamErr("Unknown master node ID", err)) c.Abort() return } diff --git a/middleware/common.go b/middleware/common.go index 8acc2c3..812dccb 100644 --- a/middleware/common.go +++ b/middleware/common.go @@ -17,7 +17,7 @@ func HashID(IDType int) gin.HandlerFunc { c.Next() return } - c.JSON(200, serializer.ParamErr("无法解析对象ID", nil)) + c.JSON(200, serializer.ParamErr("Failed to parse object ID", nil)) c.Abort() return diff --git a/middleware/frontend.go b/middleware/frontend.go index 2e08b51..95e4609 100644 --- a/middleware/frontend.go +++ b/middleware/frontend.go @@ -23,13 +23,13 @@ func FrontendFileHandler() gin.HandlerFunc { // 读取index.html file, err := bootstrap.StaticFS.Open("/index.html") if err != nil { - util.Log().Warning("静态文件[index.html]不存在,可能会影响首页展示") + util.Log().Warning("Static file \"index.html\" does not exist, it might affect the display of the homepage.") return ignoreFunc } fileContentBytes, err := ioutil.ReadAll(file) if err != nil { - util.Log().Warning("静态文件[index.html]读取失败,可能会影响首页展示") + util.Log().Warning("Cannot read static file \"index.html\", it might affect the display of the homepage.") return ignoreFunc } fileContent := string(fileContentBytes) diff --git a/middleware/session.go b/middleware/session.go index 9c3b679..dbd2d95 100644 --- a/middleware/session.go +++ b/middleware/session.go @@ -20,10 +20,10 @@ func Session(secret string) gin.HandlerFunc { var err error Store, err = redis.NewStoreWithDB(10, conf.RedisConfig.Network, conf.RedisConfig.Server, conf.RedisConfig.Password, conf.RedisConfig.DB, []byte(secret)) if err != nil { - util.Log().Panic("无法连接到 Redis:%s", err) + util.Log().Panic("Failed to connect to Redis:%s", err) } - util.Log().Info("已连接到 Redis 服务器:%s", conf.RedisConfig.Server) + util.Log().Info("Connect to Redis server %q.", conf.RedisConfig.Server) } else { Store = memstore.NewStore([]byte(secret)) } @@ -50,7 +50,7 @@ func CSRFCheck() gin.HandlerFunc { return } - c.JSON(200, serializer.Err(serializer.CodeNoPermissionErr, "来源非法", nil)) + c.JSON(200, serializer.Err(serializer.CodeNoPermissionErr, "Invalid origin", nil)) c.Abort() } } diff --git a/middleware/share.go b/middleware/share.go index 99e5647..488b703 100644 --- a/middleware/share.go +++ b/middleware/share.go @@ -16,14 +16,14 @@ func ShareOwner() gin.HandlerFunc { if userCtx, ok := c.Get("user"); ok { user = userCtx.(*model.User) } else { - c.JSON(200, serializer.Err(serializer.CodeCheckLogin, "请先登录", nil)) + c.JSON(200, serializer.Err(serializer.CodeCheckLogin, "", nil)) c.Abort() return } if share, ok := c.Get("share"); ok { if share.(*model.Share).Creator().ID != user.ID { - c.JSON(200, serializer.Err(serializer.CodeNotFound, "分享不存在", nil)) + c.JSON(200, serializer.Err(serializer.CodeShareLinkNotFound, "", nil)) c.Abort() return } @@ -46,7 +46,7 @@ func ShareAvailable() gin.HandlerFunc { share := model.GetShareByHashID(c.Param("id")) if share == nil || !share.IsAvailable() { - c.JSON(200, serializer.Err(serializer.CodeNotFound, "分享不存在或已失效", nil)) + c.JSON(200, serializer.Err(serializer.CodeShareLinkNotFound, "", nil)) c.Abort() return } @@ -65,7 +65,7 @@ func ShareCanPreview() gin.HandlerFunc { c.Next() return } - c.JSON(200, serializer.Err(serializer.CodeNoPermissionErr, "此分享无法预览", + c.JSON(200, serializer.Err(serializer.CodeDisabledSharePreview, "", nil)) c.Abort() return @@ -85,7 +85,7 @@ func CheckShareUnlocked() gin.HandlerFunc { unlocked := util.GetSession(c, sessionKey) != nil if !unlocked { c.JSON(200, serializer.Err(serializer.CodeNoPermissionErr, - "无权访问此分享", nil)) + "", nil)) c.Abort() return } @@ -109,7 +109,7 @@ func BeforeShareDownload() gin.HandlerFunc { // 检查用户是否可以下载此分享的文件 err := share.CanBeDownloadBy(user) if err != nil { - c.JSON(200, serializer.Err(serializer.CodeNoPermissionErr, err.Error(), + c.JSON(200, serializer.Err(serializer.CodeGroupNotAllowed, err.Error(), nil)) c.Abort() return @@ -118,7 +118,7 @@ func BeforeShareDownload() gin.HandlerFunc { // 对积分、下载次数进行更新 err = share.DownloadBy(user, c) if err != nil { - c.JSON(200, serializer.Err(serializer.CodeNoPermissionErr, err.Error(), + c.JSON(200, serializer.Err(serializer.CodeGroupNotAllowed, err.Error(), nil)) c.Abort() return diff --git a/pkg/serializer/error.go b/pkg/serializer/error.go index d586ac5..118aa81 100644 --- a/pkg/serializer/error.go +++ b/pkg/serializer/error.go @@ -188,6 +188,8 @@ const ( CodeQQNotLinked = 40068 // 密码不正确 CodeIncorrectPassword = 40069 + // 分享无法预览 + CodeDisabledSharePreview = 40070 // CodeDBError 数据库操作失败 CodeDBError = 50001 // CodeEncryptError 加密失败 From 7366ff534e6a8c13a84527d6e006800c79d74544 Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Thu, 29 Sep 2022 17:40:22 +0800 Subject: [PATCH 13/16] i18n: logs in models --- models/defaults.go | 4 ++-- models/download.go | 4 ++-- models/file.go | 2 +- models/folder.go | 8 ++++---- models/init.go | 6 +++--- models/migration.go | 34 +++++++++++++++++----------------- models/share.go | 6 +++--- models/tag.go | 2 +- models/task.go | 2 +- 9 files changed, 34 insertions(+), 34 deletions(-) diff --git a/models/defaults.go b/models/defaults.go index 711baac..a37ecac 100644 --- a/models/defaults.go +++ b/models/defaults.go @@ -11,9 +11,9 @@ var defaultSettings = []Setting{ {Name: "siteName", Value: `Cloudreve`, Type: "basic"}, {Name: "register_enabled", Value: `1`, Type: "register"}, {Name: "default_group", Value: `2`, Type: "register"}, - {Name: "siteKeywords", Value: `网盘,网盘`, Type: "basic"}, + {Name: "siteKeywords", Value: `Cloudreve, cloud storage`, Type: "basic"}, {Name: "siteDes", Value: `Cloudreve`, Type: "basic"}, - {Name: "siteTitle", Value: `平步云端`, Type: "basic"}, + {Name: "siteTitle", Value: `Inclusive cloud storage for everyone`, Type: "basic"}, {Name: "siteScript", Value: ``, Type: "basic"}, {Name: "siteID", Value: uuid.Must(uuid.NewV4()).String(), Type: "basic"}, {Name: "fromName", Value: `Cloudreve`, Type: "mail"}, diff --git a/models/download.go b/models/download.go index 40802ad..87f4533 100644 --- a/models/download.go +++ b/models/download.go @@ -60,7 +60,7 @@ func (task *Download) BeforeSave() (err error) { // Create 创建离线下载记录 func (task *Download) Create() (uint, error) { if err := DB.Create(task).Error; err != nil { - util.Log().Warning("无法插入离线下载记录, %s", err) + util.Log().Warning("Failed to insert download record: %s", err) return 0, err } return task.ID, nil @@ -69,7 +69,7 @@ func (task *Download) Create() (uint, error) { // Save 更新 func (task *Download) Save() error { if err := DB.Save(task).Error; err != nil { - util.Log().Warning("无法更新离线下载记录, %s", err) + util.Log().Warning("Failed to update download record: %s", err) return err } return nil diff --git a/models/file.go b/models/file.go index 34dad78..98cb071 100644 --- a/models/file.go +++ b/models/file.go @@ -43,7 +43,7 @@ func (file *File) Create() error { tx := DB.Begin() if err := tx.Create(file).Error; err != nil { - util.Log().Warning("无法插入文件记录, %s", err) + util.Log().Warning("Failed to insert file record: %s", err) tx.Rollback() return err } diff --git a/models/folder.go b/models/folder.go index fd3c241..16a0a34 100644 --- a/models/folder.go +++ b/models/folder.go @@ -161,7 +161,7 @@ func (folder *Folder) MoveOrCopyFileTo(files []uint, dstFolder *Folder, isCopy b // 复制文件记录 for _, oldFile := range originFiles { if !oldFile.CanCopy() { - util.Log().Warning("无法复制正在上传中的文件 [%s], 跳过...", oldFile.Name) + util.Log().Warning("Cannot copy file %q because it's being uploaded now, skipping...", oldFile.Name) continue } @@ -224,8 +224,8 @@ func (folder *Folder) CopyFolderTo(folderID uint, dstFolder *Folder) (size uint6 } else if IDCache, ok := newIDCache[*folder.ParentID]; ok { newID = IDCache } else { - util.Log().Warning("无法取得新的父目录:%d", folder.ParentID) - return size, errors.New("无法取得新的父目录") + util.Log().Warning("Failed to get parent folder %q", folder.ParentID) + return size, errors.New("Failed to get parent folder") } // 插入新的目录记录 @@ -254,7 +254,7 @@ func (folder *Folder) CopyFolderTo(folderID uint, dstFolder *Folder) (size uint6 // 复制文件记录 for _, oldFile := range originFiles { if !oldFile.CanCopy() { - util.Log().Warning("无法复制正在上传中的文件 [%s], 跳过...", oldFile.Name) + util.Log().Warning("Cannot copy file %q because it's being uploaded now, skipping...", oldFile.Name) continue } diff --git a/models/init.go b/models/init.go index 397317d..c2f1c1a 100644 --- a/models/init.go +++ b/models/init.go @@ -20,7 +20,7 @@ var DB *gorm.DB // Init 初始化 MySQL 链接 func Init() { - util.Log().Info("初始化数据库连接") + util.Log().Info("Initializing database connection...") var ( db *gorm.DB @@ -51,13 +51,13 @@ func Init() { conf.DatabaseConfig.Name, conf.DatabaseConfig.Charset)) default: - util.Log().Panic("不支持数据库类型: %s", conf.DatabaseConfig.Type) + util.Log().Panic("Unsupported database type %q.", conf.DatabaseConfig.Type) } } //db.SetLogger(util.Log()) if err != nil { - util.Log().Panic("连接数据库不成功, %s", err) + util.Log().Panic("Failed to connect to database: %s", err) } // 处理表前缀 diff --git a/models/migration.go b/models/migration.go index 07012b6..63ae60d 100644 --- a/models/migration.go +++ b/models/migration.go @@ -23,12 +23,12 @@ func needMigration() bool { func migration() { // 确认是否需要执行迁移 if !needMigration() { - util.Log().Info("数据库版本匹配,跳过数据库迁移") + util.Log().Info("Database version fulfilled, skip schema migration.") return } - util.Log().Info("开始进行数据库初始化...") + util.Log().Info("Start initializing database schema...") // 清除所有缓存 if instance, ok := cache.Store.(*cache.RedisStore); ok { @@ -61,7 +61,7 @@ func migration() { // 执行数据库升级脚本 execUpgradeScripts() - util.Log().Info("数据库初始化结束") + util.Log().Info("Finish initializing database schema.") } @@ -70,7 +70,7 @@ func addDefaultPolicy() { // 未找到初始存储策略时,则创建 if gorm.IsRecordNotFoundError(err) { defaultPolicy := Policy{ - Name: "默认存储策略", + Name: "Default storage policy", Type: "local", MaxSize: 0, AutoRename: true, @@ -82,7 +82,7 @@ func addDefaultPolicy() { }, } if err := DB.Create(&defaultPolicy).Error; err != nil { - util.Log().Panic("无法创建初始存储策略, %s", err) + util.Log().Panic("Failed to create default storage policy: %s", err) } } } @@ -98,7 +98,7 @@ func addDefaultGroups() { // 未找到初始管理组时,则创建 if gorm.IsRecordNotFoundError(err) { defaultAdminGroup := Group{ - Name: "管理员", + Name: "Admin", PolicyList: []uint{1}, MaxStorage: 1 * 1024 * 1024 * 1024, ShareEnabled: true, @@ -113,7 +113,7 @@ func addDefaultGroups() { }, } if err := DB.Create(&defaultAdminGroup).Error; err != nil { - util.Log().Panic("无法创建管理用户组, %s", err) + util.Log().Panic("Failed to create admin user group: %s", err) } } @@ -122,7 +122,7 @@ func addDefaultGroups() { // 未找到初始注册会员时,则创建 if gorm.IsRecordNotFoundError(err) { defaultAdminGroup := Group{ - Name: "注册会员", + Name: "User", PolicyList: []uint{1}, MaxStorage: 1 * 1024 * 1024 * 1024, ShareEnabled: true, @@ -134,7 +134,7 @@ func addDefaultGroups() { }, } if err := DB.Create(&defaultAdminGroup).Error; err != nil { - util.Log().Panic("无法创建初始注册会员用户组, %s", err) + util.Log().Panic("Failed to create initial user group: %s", err) } } @@ -143,7 +143,7 @@ func addDefaultGroups() { // 未找到初始游客用户组时,则创建 if gorm.IsRecordNotFoundError(err) { defaultAdminGroup := Group{ - Name: "游客", + Name: "Anonymous", PolicyList: []uint{}, Policies: "[]", OptionsSerialized: GroupOption{ @@ -151,7 +151,7 @@ func addDefaultGroups() { }, } if err := DB.Create(&defaultAdminGroup).Error; err != nil { - util.Log().Panic("无法创建初始游客用户组, %s", err) + util.Log().Panic("Failed to create anonymous user group: %s", err) } } } @@ -169,15 +169,15 @@ func addDefaultUser() { defaultUser.GroupID = 1 err := defaultUser.SetPassword(password) if err != nil { - util.Log().Panic("无法创建密码, %s", err) + util.Log().Panic("Failed to create password: %s", err) } if err := DB.Create(&defaultUser).Error; err != nil { - util.Log().Panic("无法创建初始用户, %s", err) + util.Log().Panic("Failed to create initial root user: %s", err) } c := color.New(color.FgWhite).Add(color.BgBlack).Add(color.Bold) - util.Log().Info("初始管理员账号:" + c.Sprint("admin@cloudreve.org")) - util.Log().Info("初始管理员密码:" + c.Sprint(password)) + util.Log().Info("Admin user name: " + c.Sprint("admin@cloudreve.org")) + util.Log().Info("Admin password: " + c.Sprint(password)) } } @@ -186,7 +186,7 @@ func addDefaultNode() { if gorm.IsRecordNotFoundError(err) { defaultAdminGroup := Node{ - Name: "主机(本机)", + Name: "Master (Local machine)", Status: NodeActive, Type: MasterNodeType, Aria2OptionsSerialized: Aria2Option{ @@ -195,7 +195,7 @@ func addDefaultNode() { }, } if err := DB.Create(&defaultAdminGroup).Error; err != nil { - util.Log().Panic("无法创建初始节点记录, %s", err) + util.Log().Panic("Failed to create initial node: %s", err) } } } diff --git a/models/share.go b/models/share.go index 137e745..750eb48 100644 --- a/models/share.go +++ b/models/share.go @@ -36,7 +36,7 @@ type Share struct { // Create 创建分享 func (share *Share) Create() (uint, error) { if err := DB.Create(share).Error; err != nil { - util.Log().Warning("无法插入数据库记录, %s", err) + util.Log().Warning("Failed to insert share record: %s", err) return 0, err } return share.ID, nil @@ -131,9 +131,9 @@ func (share *Share) CanBeDownloadBy(user *User) error { // 用户组权限 if !user.Group.OptionsSerialized.ShareDownload { if user.IsAnonymous() { - return errors.New("未登录用户无法下载") + return errors.New("you must login to download") } - return errors.New("您当前的用户组无权下载") + return errors.New("your group has no permission to download") } return nil } diff --git a/models/tag.go b/models/tag.go index 5c5250f..5ce1a4d 100644 --- a/models/tag.go +++ b/models/tag.go @@ -26,7 +26,7 @@ const ( // Create 创建标签记录 func (tag *Tag) Create() (uint, error) { if err := DB.Create(tag).Error; err != nil { - util.Log().Warning("无法插入离线下载记录, %s", err) + util.Log().Warning("Failed to insert tag record: %s", err) return 0, err } return tag.ID, nil diff --git a/models/task.go b/models/task.go index 028d552..a6fde2e 100644 --- a/models/task.go +++ b/models/task.go @@ -19,7 +19,7 @@ type Task struct { // Create 创建任务记录 func (task *Task) Create() (uint, error) { if err := DB.Create(task).Error; err != nil { - util.Log().Warning("无法插入任务记录, %s", err) + util.Log().Warning("Failed to insert task record: %s", err) return 0, err } return task.ID, nil From 9bb4a5263cf793aabf5133b97dc489ff39c34787 Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Thu, 29 Sep 2022 17:40:56 +0800 Subject: [PATCH 14/16] i18n: logs in aria2/auth/cache/cluster/serializer --- pkg/aria2/common/common.go | 4 ++-- pkg/aria2/monitor/monitor.go | 15 ++++++++------- pkg/auth/auth.go | 6 +++--- pkg/cache/driver.go | 2 +- pkg/cache/memo.go | 2 +- pkg/cache/redis.go | 2 +- pkg/cluster/errors.go | 2 +- pkg/cluster/master.go | 10 +++++----- pkg/cluster/pool.go | 4 ++-- pkg/cluster/slave.go | 22 +++++++++++----------- pkg/serializer/error.go | 2 ++ 11 files changed, 37 insertions(+), 34 deletions(-) diff --git a/pkg/aria2/common/common.go b/pkg/aria2/common/common.go index 8f281d8..bfb5fc1 100644 --- a/pkg/aria2/common/common.go +++ b/pkg/aria2/common/common.go @@ -50,9 +50,9 @@ const ( var ( // ErrNotEnabled 功能未开启错误 - ErrNotEnabled = serializer.NewError(serializer.CodeNoPermissionErr, "离线下载功能未开启", nil) + ErrNotEnabled = serializer.NewError(serializer.CodeFeatureNotEnabled, "", nil) // ErrUserNotFound 未找到下载任务创建者 - ErrUserNotFound = serializer.NewError(serializer.CodeNotFound, "无法找到任务创建者", nil) + ErrUserNotFound = serializer.NewError(serializer.CodeUserNotFound, "", nil) ) // DummyAria2 未开启Aria2功能时使用的默认处理器 diff --git a/pkg/aria2/monitor/monitor.go b/pkg/aria2/monitor/monitor.go index a515b66..32b86d8 100644 --- a/pkg/aria2/monitor/monitor.go +++ b/pkg/aria2/monitor/monitor.go @@ -45,7 +45,7 @@ func NewMonitor(task *model.Download, pool cluster.Pool, mqClient mq.MQ) { monitor.notifier = mqClient.Subscribe(monitor.Task.GID, 0) } else { - monitor.setErrorStatus(errors.New("节点不可用")) + monitor.setErrorStatus(errors.New("node not avaliable")) } } @@ -77,11 +77,12 @@ func (monitor *Monitor) Update() bool { if err != nil { monitor.retried++ - util.Log().Warning("无法获取下载任务[%s]的状态,%s", monitor.Task.GID, err) + util.Log().Warning("Cannot get status of download task %q: %s", monitor.Task.GID, err) // 十次重试后认定为任务失败 if monitor.retried > MAX_RETRY { - util.Log().Warning("无法获取下载任务[%s]的状态,超过最大重试次数限制,%s", monitor.Task.GID, err) + util.Log().Warning("Cannot get status of download task %q,exceed maximum retry threshold: %s", + monitor.Task.GID, err) monitor.setErrorStatus(err) monitor.RemoveTempFolder() return true @@ -93,7 +94,7 @@ func (monitor *Monitor) Update() bool { // 磁力链下载需要跟随 if len(status.FollowedBy) > 0 { - util.Log().Debug("离线下载[%s]重定向至[%s]", monitor.Task.GID, status.FollowedBy[0]) + util.Log().Debug("Redirected download task from %q to %q.", monitor.Task.GID, status.FollowedBy[0]) monitor.Task.GID = status.FollowedBy[0] monitor.Task.Save() return false @@ -101,13 +102,13 @@ func (monitor *Monitor) Update() bool { // 更新任务信息 if err := monitor.UpdateTaskInfo(status); err != nil { - util.Log().Warning("无法更新下载任务[%s]的任务信息[%s],", monitor.Task.GID, err) + util.Log().Warning("Failed to update status of download task %q: %s", monitor.Task.GID, err) monitor.setErrorStatus(err) monitor.RemoveTempFolder() return true } - util.Log().Debug("离线下载[%s]更新状态[%s]", status.Gid, status.Status) + util.Log().Debug("Remote download %q status updated to %q.", status.Gid, status.Status) switch status.Status { case "complete": @@ -122,7 +123,7 @@ func (monitor *Monitor) Update() bool { monitor.RemoveTempFolder() return true default: - util.Log().Warning("下载任务[%s]返回未知状态信息[%s],", monitor.Task.GID, status.Status) + util.Log().Warning("Download task %q returns unknown status %q.", monitor.Task.GID, status.Status) return true } } diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index 6b009d3..32a7e91 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -17,10 +17,10 @@ import ( ) var ( - ErrAuthFailed = serializer.NewError(serializer.CodeNoPermissionErr, "鉴权失败", nil) + ErrAuthFailed = serializer.NewError(serializer.CodeInvalidSign, "invalid sign", nil) ErrAuthHeaderMissing = serializer.NewError(serializer.CodeNoPermissionErr, "authorization header is missing", nil) ErrExpiresMissing = serializer.NewError(serializer.CodeNoPermissionErr, "expire timestamp is missing", nil) - ErrExpired = serializer.NewError(serializer.CodeSignExpired, "签名已过期", nil) + ErrExpired = serializer.NewError(serializer.CodeSignExpired, "signature expired", nil) ) const CrHeaderPrefix = "X-Cr-" @@ -136,7 +136,7 @@ func Init() { } else { secretKey = conf.SlaveConfig.Secret if secretKey == "" { - util.Log().Panic("未指定 SlaveSecret,请前往配置文件中指定") + util.Log().Panic("SlaveSecret is not set, please specify it in config file.") } } General = HMACAuth{ diff --git a/pkg/cache/driver.go b/pkg/cache/driver.go index 74c1219..5a919a7 100644 --- a/pkg/cache/driver.go +++ b/pkg/cache/driver.go @@ -24,7 +24,7 @@ func Init(isSlave bool) { if isSlave { err := Store.Sets(conf.OptionOverwrite, "setting_") if err != nil { - util.Log().Warning("无法覆盖数据库设置: %s", err) + util.Log().Warning("Failed to overwrite database setting: %s", err) } } } diff --git a/pkg/cache/memo.go b/pkg/cache/memo.go index 8b7522b..0c55ba5 100644 --- a/pkg/cache/memo.go +++ b/pkg/cache/memo.go @@ -53,7 +53,7 @@ func (store *MemoStore) GarbageCollect() { store.Store.Range(func(key, value interface{}) bool { if item, ok := value.(itemWithTTL); ok { if item.expires > 0 && item.expires < time.Now().Unix() { - util.Log().Debug("回收垃圾[%s]", key.(string)) + util.Log().Debug("Cache %q is garbage collected.", key.(string)) store.Store.Delete(key) } } diff --git a/pkg/cache/redis.go b/pkg/cache/redis.go index c2b9bb7..b9f7254 100644 --- a/pkg/cache/redis.go +++ b/pkg/cache/redis.go @@ -66,7 +66,7 @@ func NewRedisStore(size int, network, address, password, database string) *Redis redis.DialPassword(password), ) if err != nil { - util.Log().Warning("无法创建Redis连接:%s", err) + util.Log().Warning("Failed to create Redis connection: %s", err) return nil, err } return c, nil diff --git a/pkg/cluster/errors.go b/pkg/cluster/errors.go index 84b2ad8..acd21d3 100644 --- a/pkg/cluster/errors.go +++ b/pkg/cluster/errors.go @@ -8,5 +8,5 @@ import ( var ( ErrFeatureNotExist = errors.New("No nodes in nodepool match the feature specificed") ErrIlegalPath = errors.New("path out of boundary of setting temp folder") - ErrMasterNotFound = serializer.NewError(serializer.CodeMasterNotFound, "未知的主机节点", nil) + ErrMasterNotFound = serializer.NewError(serializer.CodeMasterNotFound, "Unknown master node id", nil) ) diff --git a/pkg/cluster/master.go b/pkg/cluster/master.go index 885e99a..9c3dc61 100644 --- a/pkg/cluster/master.go +++ b/pkg/cluster/master.go @@ -161,7 +161,7 @@ func (r *rpcService) Init() error { // 解析RPC服务地址 server, err := url.Parse(r.parent.Model.Aria2OptionsSerialized.Server) if err != nil { - util.Log().Warning("无法解析主机 Aria2 RPC 服务地址,%s", err) + util.Log().Warning("Failed to parse Aria2 RPC server URL: %s", err) return err } server.Path = "/jsonrpc" @@ -171,7 +171,7 @@ func (r *rpcService) Init() error { if r.parent.Model.Aria2OptionsSerialized.Options != "" { err = json.Unmarshal([]byte(r.parent.Model.Aria2OptionsSerialized.Options), &globalOptions) if err != nil { - util.Log().Warning("无法解析主机 Aria2 配置,%s", err) + util.Log().Warning("Failed to parse aria2 options: %s", err) return err } } @@ -221,7 +221,7 @@ func (r *rpcService) Status(task *model.Download) (rpc.StatusInfo, error) { res, err := r.Caller.TellStatus(task.GID) if err != nil { // 失败后重试 - util.Log().Debug("无法获取离线下载状态,%s,稍后重试", err) + util.Log().Debug("Failed to get download task status, please retry later: %s", err) time.Sleep(r.retryDuration) res, err = r.Caller.TellStatus(task.GID) } @@ -233,7 +233,7 @@ func (r *rpcService) Cancel(task *model.Download) error { // 取消下载任务 _, err := r.Caller.Remove(task.GID) if err != nil { - util.Log().Warning("无法取消离线下载任务[%s], %s", task.GID, err) + util.Log().Warning("Failed to cancel task %q: %s", task.GID, err) } return err @@ -264,7 +264,7 @@ func (s *rpcService) DeleteTempFile(task *model.Download) error { time.Sleep(d) err := os.RemoveAll(src) if err != nil { - util.Log().Warning("无法删除离线下载临时目录[%s], %s", src, err) + util.Log().Warning("Failed to delete temp download folder: %q: %s", src, err) } }(s.deletePaddingDuration, task.Parent) diff --git a/pkg/cluster/pool.go b/pkg/cluster/pool.go index 710b0a3..d6704b6 100644 --- a/pkg/cluster/pool.go +++ b/pkg/cluster/pool.go @@ -42,7 +42,7 @@ func Init() { Default = &NodePool{} Default.Init() if err := Default.initFromDB(); err != nil { - util.Log().Warning("节点池初始化失败, %s", err) + util.Log().Warning("Failed to initialize node pool: %s", err) } } @@ -83,7 +83,7 @@ func (pool *NodePool) GetNodeByID(id uint) Node { } func (pool *NodePool) nodeStatusChange(isActive bool, id uint) { - util.Log().Debug("从机节点 [ID=%d] 状态变更 [Active=%t]", id, isActive) + util.Log().Debug("Slave node [ID=%d] status changed to [Active=%t].", id, isActive) var node Node pool.lock.Lock() if n, ok := pool.inactive[id]; ok { diff --git a/pkg/cluster/slave.go b/pkg/cluster/slave.go index 79118b2..94d286b 100644 --- a/pkg/cluster/slave.go +++ b/pkg/cluster/slave.go @@ -172,7 +172,7 @@ func (node *SlaveNode) StartPingLoop() { recoverDuration := time.Duration(model.GetIntSetting("slave_recover_interval", 600)) * time.Second pingTicker := time.Duration(0) - util.Log().Debug("从机节点 [%s] 启动心跳循环", node.Model.Name) + util.Log().Debug("Slave node %q heartbeat loop started.", node.Model.Name) retry := 0 recoverMode := false isFirstLoop := true @@ -185,39 +185,39 @@ loop: pingTicker = tickDuration } - util.Log().Debug("从机节点 [%s] 发送Ping", node.Model.Name) + util.Log().Debug("Slave node %q send ping.", node.Model.Name) res, err := node.Ping(node.getHeartbeatContent(isFirstLoop)) isFirstLoop = false if err != nil { - util.Log().Debug("Ping从机节点 [%s] 时发生错误: %s", node.Model.Name, err) + util.Log().Debug("Error while ping slave node %q: %s", node.Model.Name, err) retry++ if retry >= model.GetIntSetting("slave_node_retry", 3) { - util.Log().Debug("从机节点 [%s] Ping 重试已达到最大限制,将从机节点标记为不可用", node.Model.Name) + util.Log().Debug("Retry threshold for pinging slave node %q exceeded, mark it as offline.", node.Model.Name) node.changeStatus(false) if !recoverMode { // 启动恢复监控循环 - util.Log().Debug("从机节点 [%s] 进入恢复模式", node.Model.Name) + util.Log().Debug("Slave node %q entered recovery mode.", node.Model.Name) pingTicker = recoverDuration recoverMode = true } } } else { if recoverMode { - util.Log().Debug("从机节点 [%s] 复活", node.Model.Name) + util.Log().Debug("Slave node %q recovered.", node.Model.Name) pingTicker = tickDuration recoverMode = false isFirstLoop = true } - util.Log().Debug("从机节点 [%s] 状态: %s", node.Model.Name, res) + util.Log().Debug("Status of slave node %q: %s", node.Model.Name, res) node.changeStatus(true) retry = 0 } case <-node.close: - util.Log().Debug("从机节点 [%s] 收到关闭信号", node.Model.Name) + util.Log().Debug("Slave node %q received shutdown signal.", node.Model.Name) break loop } } @@ -421,7 +421,7 @@ func RemoteCallback(url string, body serializer.UploadCallback) error { Data: body, }) if err != nil { - return serializer.NewError(serializer.CodeCallbackError, "无法编码回调正文", err) + return serializer.NewError(serializer.CodeCallbackError, "Failed to encode callback content", err) } resp := request.GeneralClient.Request( @@ -433,13 +433,13 @@ func RemoteCallback(url string, body serializer.UploadCallback) error { ) if resp.Err != nil { - return serializer.NewError(serializer.CodeCallbackError, "从机无法发起回调请求", resp.Err) + return serializer.NewError(serializer.CodeCallbackError, "Slave cannot send callback request", resp.Err) } // 解析回调服务端响应 response, err := resp.DecodeResponse() if err != nil { - msg := fmt.Sprintf("从机无法解析主机返回的响应 (StatusCode=%d)", resp.Response.StatusCode) + msg := fmt.Sprintf("Slave cannot parse callback response from master (StatusCode=%d).", resp.Response.StatusCode) return serializer.NewError(serializer.CodeCallbackError, msg, err) } diff --git a/pkg/serializer/error.go b/pkg/serializer/error.go index 118aa81..b18d6de 100644 --- a/pkg/serializer/error.go +++ b/pkg/serializer/error.go @@ -190,6 +190,8 @@ const ( CodeIncorrectPassword = 40069 // 分享无法预览 CodeDisabledSharePreview = 40070 + // 签名无效 + CodeInvalidSign = 40071 // CodeDBError 数据库操作失败 CodeDBError = 50001 // CodeEncryptError 加密失败 From 196729bae871342b4fd83704507996f5cb441a1b Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Thu, 29 Sep 2022 17:42:05 +0800 Subject: [PATCH 15/16] i18n: logs in conf/crontab/email/fs.driver --- pkg/conf/conf.go | 8 +++--- pkg/crontab/collect.go | 18 ++++++------- pkg/crontab/init.go | 6 ++--- pkg/email/init.go | 2 +- pkg/email/mail.go | 4 +-- pkg/email/smtp.go | 10 +++---- pkg/filesystem/driver/cos/handler.go | 2 +- pkg/filesystem/driver/local/handler.go | 32 +++++++++++------------ pkg/filesystem/driver/onedrive/api.go | 24 ++++++++--------- pkg/filesystem/driver/onedrive/client.go | 8 +++--- pkg/filesystem/driver/onedrive/handler.go | 2 +- pkg/filesystem/driver/onedrive/oauth.go | 2 +- pkg/filesystem/driver/oss/callback.go | 2 +- pkg/filesystem/driver/oss/handler.go | 6 ++--- pkg/filesystem/driver/remote/handler.go | 4 +-- pkg/filesystem/driver/s3/handler.go | 2 +- pkg/filesystem/driver/upyun/handler.go | 2 +- 17 files changed, 67 insertions(+), 67 deletions(-) diff --git a/pkg/conf/conf.go b/pkg/conf/conf.go index a9f7d06..c9e5c9a 100644 --- a/pkg/conf/conf.go +++ b/pkg/conf/conf.go @@ -85,13 +85,13 @@ func Init(path string) { }, defaultConf) f, err := util.CreatNestedFile(path) if err != nil { - util.Log().Panic("无法创建配置文件, %s", err) + util.Log().Panic("Failed to create config file: %s", err) } // 写入配置文件 _, err = f.WriteString(confContent) if err != nil { - util.Log().Panic("无法写入配置文件, %s", err) + util.Log().Panic("Failed to write config file: %s", err) } f.Close() @@ -99,7 +99,7 @@ func Init(path string) { cfg, err = ini.Load(path) if err != nil { - util.Log().Panic("无法解析配置文件 '%s': %s", path, err) + util.Log().Panic("Failed to parse config file %q: %s", path, err) } sections := map[string]interface{}{ @@ -114,7 +114,7 @@ func Init(path string) { for sectionName, sectionStruct := range sections { err = mapSection(sectionName, sectionStruct) if err != nil { - util.Log().Panic("配置文件 %s 分区解析失败: %s", sectionName, err) + util.Log().Panic("Failed to parse config section %q: %s", sectionName, err) } } diff --git a/pkg/crontab/collect.go b/pkg/crontab/collect.go index 79bf587..0667570 100644 --- a/pkg/crontab/collect.go +++ b/pkg/crontab/collect.go @@ -22,7 +22,7 @@ func garbageCollect() { collectCache(store) } - util.Log().Info("定时任务 [cron_garbage_collect] 执行完毕") + util.Log().Info("Crontab job \"cron_garbage_collect\" complete.") } func collectArchiveFile() { @@ -36,23 +36,23 @@ func collectArchiveFile() { if err == nil && !info.IsDir() && strings.HasPrefix(filepath.Base(path), "archive_") && time.Now().Sub(info.ModTime()).Seconds() > float64(expires) { - util.Log().Debug("删除过期打包下载临时文件 [%s]", path) + util.Log().Debug("Delete expired batch download temp file %q.", path) // 删除符合条件的文件 if err := os.Remove(path); err != nil { - util.Log().Debug("临时文件 [%s] 删除失败 , %s", path, err) + util.Log().Debug("Failed to delete temp file %q: %s", path, err) } } return nil }) if err != nil { - util.Log().Debug("[定时任务] 无法列取临时打包目录") + util.Log().Debug("Crontab job cannot list temp batch download folder: %s", err) } } func collectCache(store *cache.MemoStore) { - util.Log().Debug("清理内存缓存") + util.Log().Debug("Cleanup memory cache.") store.GarbageCollect() } @@ -78,22 +78,22 @@ func uploadSessionCollect() { for uid, filesIDs := range userToFiles { user, err := model.GetUserByID(uid) if err != nil { - util.Log().Warning("上传会话所属用户不存在, %s", err) + util.Log().Warning("Owner of the upload session cannot be found: %s", err) continue } fs, err := filesystem.NewFileSystem(&user) if err != nil { - util.Log().Warning("无法初始化文件系统, %s", err) + util.Log().Warning("Failed to initialize filesystem: %s", err) continue } if err = fs.Delete(context.Background(), []uint{}, filesIDs, false); err != nil { - util.Log().Warning("无法删除上传会话, %s", err) + util.Log().Warning("Failed to delete upload session: %s", err) } fs.Recycle() } - util.Log().Info("定时任务 [cron_recycle_upload_session] 执行完毕") + util.Log().Info("Crontab job \"cron_recycle_upload_session\" complete.") } diff --git a/pkg/crontab/init.go b/pkg/crontab/init.go index 0a696a4..5971c2c 100644 --- a/pkg/crontab/init.go +++ b/pkg/crontab/init.go @@ -19,7 +19,7 @@ func Reload() { // Init 初始化定时任务 func Init() { - util.Log().Info("初始化定时任务...") + util.Log().Info("Initialize crontab jobs...") // 读取cron日程设置 options := model.GetSettingByNames( "cron_garbage_collect", @@ -34,12 +34,12 @@ func Init() { case "cron_recycle_upload_session": handler = uploadSessionCollect default: - util.Log().Warning("未知定时任务类型 [%s],跳过", k) + util.Log().Warning("Unknown crontab job type %q, skipping...", k) continue } if _, err := Cron.AddFunc(v, handler); err != nil { - util.Log().Warning("无法启动定时任务 [%s] , %s", k, err) + util.Log().Warning("Failed to start crontab job %q: %s", k, err) } } diff --git a/pkg/email/init.go b/pkg/email/init.go index 64771a9..fe83fe3 100644 --- a/pkg/email/init.go +++ b/pkg/email/init.go @@ -15,7 +15,7 @@ var Lock sync.RWMutex // Init 初始化 func Init() { - util.Log().Debug("邮件队列初始化") + util.Log().Debug("Initializing email sending queue...") Lock.Lock() defer Lock.Unlock() diff --git a/pkg/email/mail.go b/pkg/email/mail.go index 01cc690..fbcbd68 100644 --- a/pkg/email/mail.go +++ b/pkg/email/mail.go @@ -15,9 +15,9 @@ type Driver interface { var ( // ErrChanNotOpen 邮件队列未开启 - ErrChanNotOpen = errors.New("邮件队列未开启") + ErrChanNotOpen = errors.New("email queue is not started") // ErrNoActiveDriver 无可用邮件发送服务 - ErrNoActiveDriver = errors.New("无可用邮件发送服务") + ErrNoActiveDriver = errors.New("no avaliable email provider") ) // Send 发送邮件 diff --git a/pkg/email/smtp.go b/pkg/email/smtp.go index 02376d7..c92cce7 100644 --- a/pkg/email/smtp.go +++ b/pkg/email/smtp.go @@ -68,7 +68,7 @@ func (client *SMTP) Init() { defer func() { if err := recover(); err != nil { client.chOpen = false - util.Log().Error("邮件发送队列出现异常, %s ,10 秒后重置", err) + util.Log().Error("Exception while sending email: %s, queue will be reset in 10 seconds.", err) time.Sleep(time.Duration(10) * time.Second) client.Init() } @@ -91,7 +91,7 @@ func (client *SMTP) Init() { select { case m, ok := <-client.ch: if !ok { - util.Log().Debug("邮件队列关闭") + util.Log().Debug("Email queue closing...") client.chOpen = false return } @@ -102,15 +102,15 @@ func (client *SMTP) Init() { open = true } if err := mail.Send(s, m); err != nil { - util.Log().Warning("邮件发送失败, %s", err) + util.Log().Warning("Failed to send email: %s", err) } else { - util.Log().Debug("邮件已发送") + util.Log().Debug("Email sent.") } // 长时间没有新邮件,则关闭SMTP连接 case <-time.After(time.Duration(client.Config.Keepalive) * time.Second): if open { if err := s.Close(); err != nil { - util.Log().Warning("无法关闭 SMTP 连接 %s", err) + util.Log().Warning("Failed to close SMTP connection: %s", err) } open = false } diff --git a/pkg/filesystem/driver/cos/handler.go b/pkg/filesystem/driver/cos/handler.go index 5389641..48ff09c 100644 --- a/pkg/filesystem/driver/cos/handler.go +++ b/pkg/filesystem/driver/cos/handler.go @@ -218,7 +218,7 @@ func (handler Driver) Delete(ctx context.Context, files []string) ([]string, err return failed, nil } - return failed, errors.New("删除失败") + return failed, errors.New("delete failed") } // Thumb 获取文件缩略图 diff --git a/pkg/filesystem/driver/local/handler.go b/pkg/filesystem/driver/local/handler.go index e5e8994..2c1205c 100644 --- a/pkg/filesystem/driver/local/handler.go +++ b/pkg/filesystem/driver/local/handler.go @@ -43,7 +43,7 @@ func (handler Driver) List(ctx context.Context, path string, recursive bool) ([] } if err != nil { - util.Log().Warning("无法遍历目录 %s, %s", path, err) + util.Log().Warning("Failed to walk folder %q: %s", path, err) return filepath.SkipDir } @@ -78,7 +78,7 @@ func (handler Driver) Get(ctx context.Context, path string) (response.RSCloser, // 打开文件 file, err := os.Open(util.RelativePath(path)) if err != nil { - util.Log().Debug("无法打开文件:%s", err) + util.Log().Debug("Failed to open file: %s", err) return nil, err } @@ -94,8 +94,8 @@ func (handler Driver) Put(ctx context.Context, file fsctx.FileHeader) error { // 如果非 Overwrite,则检查是否有重名冲突 if fileInfo.Mode&fsctx.Overwrite != fsctx.Overwrite { if util.Exists(dst) { - util.Log().Warning("物理同名文件已存在或不可用: %s", dst) - return errors.New("物理同名文件已存在或不可用") + util.Log().Warning("File with the same name existed or unavailable: %s", dst) + return errors.New("file with the same name existed or unavailable") } } @@ -104,7 +104,7 @@ func (handler Driver) Put(ctx context.Context, file fsctx.FileHeader) error { if !util.Exists(basePath) { err := os.MkdirAll(basePath, Perm) if err != nil { - util.Log().Warning("无法创建目录,%s", err) + util.Log().Warning("Failed to create directory: %s", err) return err } } @@ -123,7 +123,7 @@ func (handler Driver) Put(ctx context.Context, file fsctx.FileHeader) error { out, err = os.OpenFile(dst, openMode, Perm) if err != nil { - util.Log().Warning("无法打开或创建文件,%s", err) + util.Log().Warning("Failed to open or create file: %s", err) return err } defer out.Close() @@ -131,22 +131,22 @@ func (handler Driver) Put(ctx context.Context, file fsctx.FileHeader) error { if fileInfo.Mode&fsctx.Append == fsctx.Append { stat, err := out.Stat() if err != nil { - util.Log().Warning("无法读取文件信息,%s", err) + util.Log().Warning("Failed to read file info: %s", err) return err } if uint64(stat.Size()) < fileInfo.AppendStart { - return errors.New("未上传完成的文件分片与预期大小不一致") + return errors.New("size of unfinished uploaded chunks is not as expected") } else if uint64(stat.Size()) > fileInfo.AppendStart { out.Close() if err := handler.Truncate(ctx, dst, fileInfo.AppendStart); err != nil { - return fmt.Errorf("覆盖分片时发生错误: %w", err) + return fmt.Errorf("failed to overwrite chunk: %w", err) } out, err = os.OpenFile(dst, openMode, Perm) defer out.Close() if err != nil { - util.Log().Warning("无法打开或创建文件,%s", err) + util.Log().Warning("Failed to create or open file: %s", err) return err } } @@ -158,10 +158,10 @@ func (handler Driver) Put(ctx context.Context, file fsctx.FileHeader) error { } func (handler Driver) Truncate(ctx context.Context, src string, size uint64) error { - util.Log().Warning("截断文件 [%s] 至 [%d]", src, size) + util.Log().Warning("Truncate file %q to [%d].", src, size) out, err := os.OpenFile(src, os.O_WRONLY, Perm) if err != nil { - util.Log().Warning("无法打开文件,%s", err) + util.Log().Warning("Failed to open file: %s", err) return err } @@ -180,7 +180,7 @@ func (handler Driver) Delete(ctx context.Context, files []string) ([]string, err if util.Exists(filePath) { err := os.Remove(filePath) if err != nil { - util.Log().Warning("无法删除文件,%s", err) + util.Log().Warning("Failed to delete file: %s", err) retErr = err deleteFailed = append(deleteFailed, value) } @@ -217,7 +217,7 @@ func (handler Driver) Source( ) (string, error) { file, ok := ctx.Value(fsctx.FileModelCtx).(model.File) if !ok { - return "", errors.New("无法获取文件记录上下文") + return "", errors.New("failed to read file model context") } // 是否启用了CDN @@ -238,7 +238,7 @@ func (handler Driver) Source( downloadSessionID := util.RandStringRunes(16) err = cache.Set("download_"+downloadSessionID, file, int(ttl)) if err != nil { - return "", serializer.NewError(serializer.CodeCacheOperation, "无法创建下载会话", err) + return "", serializer.NewError(serializer.CodeCacheOperation, "Failed to create download session", err) } // 签名生成文件记录 @@ -257,7 +257,7 @@ func (handler Driver) Source( } if err != nil { - return "", serializer.NewError(serializer.CodeEncryptError, "无法对URL进行签名", err) + return "", serializer.NewError(serializer.CodeEncryptError, "Failed to sign url", err) } finalURL := baseURL.ResolveReference(signedURI).String() diff --git a/pkg/filesystem/driver/onedrive/api.go b/pkg/filesystem/driver/onedrive/api.go index 438459a..1e41b72 100644 --- a/pkg/filesystem/driver/onedrive/api.go +++ b/pkg/filesystem/driver/onedrive/api.go @@ -95,7 +95,7 @@ func (client *Client) ListChildren(ctx context.Context, path string) ([]FileInfo } if retried < ListRetry { retried++ - util.Log().Debug("路径[%s]列取请求失败[%s],5秒钟后重试", path, err) + util.Log().Debug("Failed to list path %q: %s, will retry in 5 seconds.", path, err) time.Sleep(time.Duration(5) * time.Second) return client.ListChildren(context.WithValue(ctx, fsctx.RetryCtx, retried), path) } @@ -445,7 +445,7 @@ func (client *Client) GetThumbURL(ctx context.Context, dst string, w, h uint) (s } } - return "", errors.New("无法生成缩略图") + return "", errors.New("failed to generate thumb") } // MonitorUpload 监控客户端分片上传进度 @@ -460,39 +460,39 @@ func (client *Client) MonitorUpload(uploadURL, callbackKey, path string, size ui for { select { case <-callbackChan: - util.Log().Debug("客户端完成回调") + util.Log().Debug("Client finished OneDrive callback.") return case <-time.After(time.Duration(ttl) * time.Second): // 上传会话到期,仍未完成上传,创建占位符 client.DeleteUploadSession(context.Background(), uploadURL) _, err := client.SimpleUpload(context.Background(), path, strings.NewReader(""), 0, WithConflictBehavior("replace")) if err != nil { - util.Log().Debug("无法创建占位文件,%s", err) + util.Log().Debug("Failed to create placeholder file: %s", err) } return case <-time.After(time.Duration(timeout) * time.Second): - util.Log().Debug("检查上传情况") + util.Log().Debug("Checking OneDrive upload status.") status, err := client.GetUploadSessionStatus(context.Background(), uploadURL) if err != nil { if resErr, ok := err.(*RespError); ok { if resErr.APIError.Code == "itemNotFound" { - util.Log().Debug("上传会话已完成,稍后检查回调") + util.Log().Debug("Upload completed, will check upload callback later.") select { case <-time.After(time.Duration(interval) * time.Second): - util.Log().Warning("未发送回调,删除文件") + util.Log().Warning("No callback is made, file will be deleted.") cache.Deletes([]string{callbackKey}, "callback_") _, err = client.Delete(context.Background(), []string{path}) if err != nil { - util.Log().Warning("无法删除未回调的文件,%s", err) + util.Log().Warning("Failed to delete file without callback: %s", err) } case <-callbackChan: - util.Log().Debug("客户端完成回调") + util.Log().Debug("Client finished callback.") } return } } - util.Log().Debug("无法获取上传会话状态,继续下一轮,%s", err.Error()) + util.Log().Debug("Failed to get upload session status: %s, continue next iteration.", err.Error()) continue } @@ -509,7 +509,7 @@ func (client *Client) MonitorUpload(uploadURL, callbackKey, path string, size ui } uploadFullSize, _ := strconv.ParseUint(sizeRange[1], 10, 64) if (sizeRange[0] == "0" && sizeRange[1] == "") || uploadFullSize+1 != size { - util.Log().Debug("未开始上传或文件大小不一致,取消上传会话") + util.Log().Debug("Upload has not started, or uploaded file size not match, canceling upload session...") // 取消上传会话,实测OneDrive取消上传会话后,客户端还是可以上传, // 所以上传一个空文件占位,阻止客户端上传 client.DeleteUploadSession(context.Background(), uploadURL) @@ -577,7 +577,7 @@ func (client *Client) request(ctx context.Context, method string, url string, bo if res.Response.StatusCode < 200 || res.Response.StatusCode >= 300 { decodeErr = json.Unmarshal([]byte(respBody), &errResp) if decodeErr != nil { - util.Log().Debug("Onedrive返回未知响应[%s]", respBody) + util.Log().Debug("Onedrive returns unknown response: %s", respBody) return "", sysError(decodeErr) } return "", &errResp diff --git a/pkg/filesystem/driver/onedrive/client.go b/pkg/filesystem/driver/onedrive/client.go index dfd205c..149fcf0 100644 --- a/pkg/filesystem/driver/onedrive/client.go +++ b/pkg/filesystem/driver/onedrive/client.go @@ -10,13 +10,13 @@ import ( var ( // ErrAuthEndpoint 无法解析授权端点地址 - ErrAuthEndpoint = errors.New("无法解析授权端点地址") + ErrAuthEndpoint = errors.New("failed to parse endpoint url") // ErrInvalidRefreshToken 上传策略无有效的RefreshToken - ErrInvalidRefreshToken = errors.New("上传策略无有效的RefreshToken") + ErrInvalidRefreshToken = errors.New("no valid refresh token in this policy") // ErrDeleteFile 无法删除文件 - ErrDeleteFile = errors.New("无法删除文件") + ErrDeleteFile = errors.New("cannot delete file") // ErrClientCanceled 客户端取消操作 - ErrClientCanceled = errors.New("客户端取消操作") + ErrClientCanceled = errors.New("client canceled") ) // Client OneDrive客户端 diff --git a/pkg/filesystem/driver/onedrive/handler.go b/pkg/filesystem/driver/onedrive/handler.go index 8eec3b3..98b2ba7 100644 --- a/pkg/filesystem/driver/onedrive/handler.go +++ b/pkg/filesystem/driver/onedrive/handler.go @@ -143,7 +143,7 @@ func (handler Driver) Thumb(ctx context.Context, path string) (*response.Content ok = false ) if thumbSize, ok = ctx.Value(fsctx.ThumbSizeCtx).([2]uint); !ok { - return nil, errors.New("无法获取缩略图尺寸设置") + return nil, errors.New("failed to get thumbnail size") } res, err := handler.Client.GetThumbURL(ctx, path, thumbSize[0], thumbSize[1]) diff --git a/pkg/filesystem/driver/onedrive/oauth.go b/pkg/filesystem/driver/onedrive/oauth.go index d7edbb5..914a498 100644 --- a/pkg/filesystem/driver/onedrive/oauth.go +++ b/pkg/filesystem/driver/onedrive/oauth.go @@ -152,7 +152,7 @@ func (client *Client) UpdateCredential(ctx context.Context, isSlave bool) error // 获取新的凭证 if client.Credential == nil || client.Credential.RefreshToken == "" { // 无有效的RefreshToken - util.Log().Error("上传策略[%s]凭证刷新失败,请重新授权OneDrive账号", client.Policy.Name) + util.Log().Error("Failed to refresh credential for policy %q, please login your Microsoft account again.", client.Policy.Name) return ErrInvalidRefreshToken } diff --git a/pkg/filesystem/driver/oss/callback.go b/pkg/filesystem/driver/oss/callback.go index e5b41bb..b2a8803 100644 --- a/pkg/filesystem/driver/oss/callback.go +++ b/pkg/filesystem/driver/oss/callback.go @@ -38,7 +38,7 @@ func GetPublicKey(r *http.Request) ([]byte, error) { // 确保这个 public key 是由 OSS 颁发的 if !strings.HasPrefix(string(pubURL), "http://gosspublic.alicdn.com/") && !strings.HasPrefix(string(pubURL), "https://gosspublic.alicdn.com/") { - return pubKey, errors.New("公钥URL无效") + return pubKey, errors.New("public key url invalid") } // 获取公钥 diff --git a/pkg/filesystem/driver/oss/handler.go b/pkg/filesystem/driver/oss/handler.go index 62bccbc..af27816 100644 --- a/pkg/filesystem/driver/oss/handler.go +++ b/pkg/filesystem/driver/oss/handler.go @@ -91,7 +91,7 @@ func (handler *Driver) CORS() error { // InitOSSClient 初始化OSS鉴权客户端 func (handler *Driver) InitOSSClient(forceUsePublicEndpoint bool) error { if handler.Policy == nil { - return errors.New("存储策略为空") + return errors.New("empty policy") } // 决定是否使用内网 Endpoint @@ -286,7 +286,7 @@ func (handler *Driver) Delete(ctx context.Context, files []string) ([]string, er // 统计未删除的文件 failed := util.SliceDifference(files, delRes.DeletedObjects) if len(failed) > 0 { - return failed, errors.New("删除失败") + return failed, errors.New("failed to delete") } return []string{}, nil @@ -304,7 +304,7 @@ func (handler *Driver) Thumb(ctx context.Context, path string) (*response.Conten ok = false ) if thumbSize, ok = ctx.Value(fsctx.ThumbSizeCtx).([2]uint); !ok { - return nil, errors.New("无法获取缩略图尺寸设置") + return nil, errors.New("failed to get thumbnail size") } thumbParam := fmt.Sprintf("image/resize,m_lfit,h_%d,w_%d", thumbSize[1], thumbSize[0]) diff --git a/pkg/filesystem/driver/remote/handler.go b/pkg/filesystem/driver/remote/handler.go index 7594753..9b88e8d 100644 --- a/pkg/filesystem/driver/remote/handler.go +++ b/pkg/filesystem/driver/remote/handler.go @@ -197,7 +197,7 @@ func (handler *Driver) Delete(ctx context.Context, files []string) ([]string, er return failedResp.Files, errors.New(reqResp.Error) } } - return files, errors.New("未知的返回结果格式") + return files, errors.New("unknown format of returned response") } return []string{}, nil @@ -265,7 +265,7 @@ func (handler *Driver) Source( ) if err != nil { - return "", serializer.NewError(serializer.CodeEncryptError, "无法对URL进行签名", err) + return "", serializer.NewError(serializer.CodeEncryptError, "Failed to sign URL", err) } finalURL := serverURL.ResolveReference(signedURI).String() diff --git a/pkg/filesystem/driver/s3/handler.go b/pkg/filesystem/driver/s3/handler.go index fe6007a..a6d17bf 100644 --- a/pkg/filesystem/driver/s3/handler.go +++ b/pkg/filesystem/driver/s3/handler.go @@ -62,7 +62,7 @@ func NewDriver(policy *model.Policy) (*Driver, error) { // InitS3Client 初始化S3会话 func (handler *Driver) InitS3Client() error { if handler.Policy == nil { - return errors.New("存储策略为空") + return errors.New("empty policy") } if handler.svc == nil { diff --git a/pkg/filesystem/driver/upyun/handler.go b/pkg/filesystem/driver/upyun/handler.go index 0357f72..8fed080 100644 --- a/pkg/filesystem/driver/upyun/handler.go +++ b/pkg/filesystem/driver/upyun/handler.go @@ -226,7 +226,7 @@ func (handler Driver) Thumb(ctx context.Context, path string) (*response.Content ok = false ) if thumbSize, ok = ctx.Value(fsctx.ThumbSizeCtx).([2]uint); !ok { - return nil, errors.New("无法获取缩略图尺寸设置") + return nil, errors.New("failed to get thumbnail size") } thumbParam := fmt.Sprintf("!/fwfh/%dx%d", thumbSize[0], thumbSize[1]) From 28c2ffe72ea6a6daec460319f98ae3b0b25124aa Mon Sep 17 00:00:00 2001 From: HFO4 <912394456@qq.com> Date: Thu, 29 Sep 2022 17:42:23 +0800 Subject: [PATCH 16/16] i18n: logs in filesystem --- pkg/filesystem/archive.go | 18 +++++++++--------- pkg/filesystem/file.go | 10 +++++----- pkg/filesystem/filesystem.go | 2 +- pkg/filesystem/hooks.go | 4 ++-- pkg/filesystem/image.go | 10 +++++----- pkg/filesystem/upload.go | 6 +++--- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/pkg/filesystem/archive.go b/pkg/filesystem/archive.go index 7e62590..78fc45f 100644 --- a/pkg/filesystem/archive.go +++ b/pkg/filesystem/archive.go @@ -107,7 +107,7 @@ func (fs *FileSystem) doCompress(ctx context.Context, file *model.File, folder * fs.Policy = file.GetPolicy() err := fs.DispatchHandler() if err != nil { - util.Log().Warning("无法压缩文件%s,%s", file.Name, err) + util.Log().Warning("Failed to compress file %q: %s", file.Name, err) return } @@ -117,7 +117,7 @@ func (fs *FileSystem) doCompress(ctx context.Context, file *model.File, folder * file.SourceName, ) if err != nil { - util.Log().Debug("Open%s,%s", file.Name, err) + util.Log().Debug("Failed to open %q: %s", file.Name, err) return } if closer, ok := fileToZip.(io.Closer); ok { @@ -176,7 +176,7 @@ func (fs *FileSystem) Decompress(ctx context.Context, src, dst, encoding string) // 结束时删除临时压缩文件 if tempZipFilePath != "" { if err := os.Remove(tempZipFilePath); err != nil { - util.Log().Warning("无法删除临时压缩文件 %s , %s", tempZipFilePath, err) + util.Log().Warning("Failed to delete temp archive file %q: %s", tempZipFilePath, err) } } }() @@ -197,7 +197,7 @@ func (fs *FileSystem) Decompress(ctx context.Context, src, dst, encoding string) zipFile, err := util.CreatNestedFile(tempZipFilePath) if err != nil { - util.Log().Warning("无法创建临时压缩文件 %s , %s", tempZipFilePath, err) + util.Log().Warning("Failed to create temp archive file %q: %s", tempZipFilePath, err) tempZipFilePath = "" return err } @@ -206,7 +206,7 @@ func (fs *FileSystem) Decompress(ctx context.Context, src, dst, encoding string) // 下载前先判断是否是可解压的格式 format, readStream, err := archiver.Identify(fs.FileTarget[0].SourceName, fileStream) if err != nil { - util.Log().Warning("无法识别文件格式 %s , %s", fs.FileTarget[0].SourceName, err) + util.Log().Warning("Failed to detect compressed format of file %q: %s", fs.FileTarget[0].SourceName, err) return err } @@ -228,7 +228,7 @@ func (fs *FileSystem) Decompress(ctx context.Context, src, dst, encoding string) if isZip { _, err = io.Copy(zipFile, readStream) if err != nil { - util.Log().Warning("无法写入临时压缩文件 %s , %s", tempZipFilePath, err) + util.Log().Warning("Failed to write temp archive file %q: %s", tempZipFilePath, err) return err } @@ -261,7 +261,7 @@ func (fs *FileSystem) Decompress(ctx context.Context, src, dst, encoding string) wg.Done() } if err := recover(); err != nil { - util.Log().Warning("上传压缩包内文件时出错") + util.Log().Warning("Error while uploading files inside of archive file.") fmt.Println(err) } }() @@ -274,7 +274,7 @@ func (fs *FileSystem) Decompress(ctx context.Context, src, dst, encoding string) }, true) fileStream.Close() if err != nil { - util.Log().Debug("无法上传压缩包内的文件%s , %s , 跳过", rawPath, err) + util.Log().Debug("Failed to upload file %q in archive file: %s, skipping...", rawPath, err) } } @@ -297,7 +297,7 @@ func (fs *FileSystem) Decompress(ctx context.Context, src, dst, encoding string) // 上传文件 fileStream, err := f.Open() if err != nil { - util.Log().Warning("无法打开压缩包内文件%s , %s , 跳过", rawPath, err) + util.Log().Warning("Failed to open file %q in archive file: %s, skipping...", rawPath, err) return nil } diff --git a/pkg/filesystem/file.go b/pkg/filesystem/file.go index 457e625..45f721c 100644 --- a/pkg/filesystem/file.go +++ b/pkg/filesystem/file.go @@ -72,7 +72,7 @@ func (fs *FileSystem) AddFile(ctx context.Context, parent *model.Folder, file fs if err != nil { if err := fs.Trigger(ctx, "AfterValidateFailed", file); err != nil { - util.Log().Debug("AfterValidateFailed 钩子执行失败,%s", err) + util.Log().Debug("AfterValidateFailed hook execution failed: %s", err) } return nil, ErrFileExisted.WithError(err) } @@ -203,7 +203,7 @@ func (fs *FileSystem) deleteGroupedFile(ctx context.Context, files map[uint][]*m // 取消上传会话 for _, upSession := range uploadSessions { if err := fs.Handler.CancelToken(ctx, upSession); err != nil { - util.Log().Warning("无法取消 [%s] 的上传会话: %s", upSession.Name, err) + util.Log().Warning("Failed to cancel upload session for %q: %s", upSession.Name, err) } cache.Deletes([]string{upSession.Key}, UploadSessionCachePrefix) @@ -270,14 +270,14 @@ func (fs *FileSystem) GetSource(ctx context.Context, fileID uint) (string, error if !fs.Policy.IsOriginLinkEnable { return "", serializer.NewError( serializer.CodePolicyNotAllowed, - "当前存储策略无法获得外链", + "This policy is not enabled for getting source link", nil, ) } source, err := fs.SignURL(ctx, &fs.FileTarget[0], 0, false) if err != nil { - return "", serializer.NewError(serializer.CodeNotSet, "无法获取外链", err) + return "", serializer.NewError(serializer.CodeNotSet, "Failed to get source link", err) } return source, nil @@ -298,7 +298,7 @@ func (fs *FileSystem) SignURL(ctx context.Context, file *model.File, ttl int64, siteURL := model.GetSiteURL() source, err := fs.Handler.Source(ctx, fs.FileTarget[0].SourceName, *siteURL, ttl, isDownload, fs.User.Group.SpeedLimit) if err != nil { - return "", serializer.NewError(serializer.CodeNotSet, "无法获取外链", err) + return "", serializer.NewError(serializer.CodeNotSet, "Failed to get source link", err) } return source, nil diff --git a/pkg/filesystem/filesystem.go b/pkg/filesystem/filesystem.go index b0da90e..f1745b6 100644 --- a/pkg/filesystem/filesystem.go +++ b/pkg/filesystem/filesystem.go @@ -203,7 +203,7 @@ func NewFileSystemFromCallback(c *gin.Context) (*FileSystem, error) { // 获取回调会话 callbackSessionRaw, ok := c.Get(UploadSessionCtx) if !ok { - return nil, errors.New("找不到回调会话") + return nil, errors.New("upload session not exist") } callbackSession := callbackSessionRaw.(*serializer.UploadSession) diff --git a/pkg/filesystem/hooks.go b/pkg/filesystem/hooks.go index 902dcce..4f3b9f2 100644 --- a/pkg/filesystem/hooks.go +++ b/pkg/filesystem/hooks.go @@ -44,7 +44,7 @@ func (fs *FileSystem) Trigger(ctx context.Context, name string, file fsctx.FileH for _, hook := range hooks { err := hook(ctx, fs, file) if err != nil { - util.Log().Warning("钩子执行失败:%s", err) + util.Log().Warning("Failed to execute hook:%s", err) return err } } @@ -112,7 +112,7 @@ func HookDeleteTempFile(ctx context.Context, fs *FileSystem, file fsctx.FileHead // 删除临时文件 _, err := fs.Handler.Delete(ctx, []string{file.Info().SavePath}) if err != nil { - util.Log().Warning("无法清理上传临时文件,%s", err) + util.Log().Warning("Failed to clean-up temp files: %s", err) } return nil diff --git a/pkg/filesystem/image.go b/pkg/filesystem/image.go index 30e80bf..493f8d7 100644 --- a/pkg/filesystem/image.go +++ b/pkg/filesystem/image.go @@ -71,16 +71,16 @@ func getThumbWorker() *Pool { thumbPool = &Pool{ worker: make(chan int, maxWorker), } - util.Log().Debug("初始化Thumb任务队列,WorkerNum = %d", maxWorker) + util.Log().Debug("Initialize thumbnails task queue with: WorkerNum = %d", maxWorker) }) return thumbPool } func (pool *Pool) addWorker() { pool.worker <- 1 - util.Log().Debug("Thumb任务队列,addWorker") + util.Log().Debug("Worker added to thumbnails task queue.") } func (pool *Pool) releaseWorker() { - util.Log().Debug("Thumb任务队列,releaseWorker") + util.Log().Debug("Worker released from thumbnails task queue.") <-pool.worker } @@ -107,7 +107,7 @@ func (fs *FileSystem) GenerateThumbnail(ctx context.Context, file *model.File) { image, err := thumb.NewThumbFromFile(source, file.Name) if err != nil { - util.Log().Warning("生成缩略图时无法解析 [%s] 图像数据:%s", file.SourceName, err) + util.Log().Warning("Cannot generate thumb because of failed to parse image %q: %s", file.SourceName, err) return } @@ -125,7 +125,7 @@ func (fs *FileSystem) GenerateThumbnail(ctx context.Context, file *model.File) { } if err != nil { - util.Log().Warning("无法保存缩略图:%s", err) + util.Log().Warning("Failed to save thumb: %s", err) return } diff --git a/pkg/filesystem/upload.go b/pkg/filesystem/upload.go index 50d22ff..9073ab2 100644 --- a/pkg/filesystem/upload.go +++ b/pkg/filesystem/upload.go @@ -69,7 +69,7 @@ func (fs *FileSystem) Upload(ctx context.Context, file *fsctx.FileStream) (err e followUpErr := fs.Trigger(ctx, "AfterValidateFailed", file) // 失败后再失败... if followUpErr != nil { - util.Log().Debug("AfterValidateFailed 钩子执行失败,%s", followUpErr) + util.Log().Debug("AfterValidateFailed hook execution failed: %s", followUpErr) } return err @@ -113,13 +113,13 @@ func (fs *FileSystem) CancelUpload(ctx context.Context, path string, file fsctx. // 客户端正常关闭,不执行操作 default: // 客户端取消上传,删除临时文件 - util.Log().Debug("客户端取消上传") + util.Log().Debug("Client canceled upload.") if fs.Hooks["AfterUploadCanceled"] == nil { return } err := fs.Trigger(ctx, "AfterUploadCanceled", file) if err != nil { - util.Log().Debug("执行 AfterUploadCanceled 钩子出错,%s", err) + util.Log().Debug("AfterUploadCanceled hook execution failed: %s", err) } }