diff --git a/code/rest/alien_controller.go b/code/rest/alien_controller.go index 8b4e310..fdc3ac4 100644 --- a/code/rest/alien_controller.go +++ b/code/rest/alien_controller.go @@ -2,7 +2,6 @@ package rest import ( "github.com/eyebluecn/tank/code/core" - "github.com/eyebluecn/tank/code/tool/i18n" "github.com/eyebluecn/tank/code/tool/result" "github.com/eyebluecn/tank/code/tool/util" "net/http" @@ -119,11 +118,7 @@ func (this *AlienController) FetchUploadToken(writer http.ResponseWriter, reques //store dir path dirPath := request.FormValue("dirPath") - if filename == "" { - panic(result.BadRequest("filename cannot be null")) - } else if m, _ := regexp.MatchString(MATTER_NAME_PATTERN, filename); m { - panic(result.BadRequestI18n(request, i18n.MatterNameContainSpecialChars)) - } + filename = CheckMatterName(request, filename) var expireTime time.Time if expireTimeStr == "" { @@ -288,11 +283,7 @@ func (this *AlienController) CrawlDirect(writer http.ResponseWriter, request *ht dirPath := request.FormValue("dirPath") url := request.FormValue("url") - if filename == "" { - panic(result.BadRequest("filename cannot be null.")) - } else if m, _ := regexp.MatchString(MATTER_NAME_PATTERN, filename); m { - panic(result.BadRequestI18n(request, i18n.MatterNameContainSpecialChars)) - } + filename = CheckMatterName(request, filename) var privacy bool if privacyStr == TRUE { diff --git a/code/rest/alien_service.go b/code/rest/alien_service.go index a989af3..06f817f 100644 --- a/code/rest/alien_service.go +++ b/code/rest/alien_service.go @@ -111,7 +111,7 @@ func (this *AlienService) PreviewOrDownload( shareCode := request.FormValue("shareCode") shareRootUuid := request.FormValue("shareRootUuid") - this.shareService.ValidateMatter(shareUuid, shareCode, operator, shareRootUuid, matter) + this.shareService.ValidateMatter(request, shareUuid, shareCode, operator, shareRootUuid, matter) } } diff --git a/code/rest/dashboard_service.go b/code/rest/dashboard_service.go index cff2812..12caafd 100644 --- a/code/rest/dashboard_service.go +++ b/code/rest/dashboard_service.go @@ -71,7 +71,7 @@ func (this *DashboardService) etl() { longTimeAgo := time.Now() longTimeAgo = longTimeAgo.AddDate(-20, 0, 0) - this.logger.Info("Time %s -> %s", util.ConvertTimeToDateTimeString(startTime), util.ConvertTimeToDateTimeString(endTime)) + this.logger.Info("ETL dashboard data from %s to %s", util.ConvertTimeToDateTimeString(startTime), util.ConvertTimeToDateTimeString(endTime)) //check whether the record has created. dbDashboard := this.dashboardDao.FindByDt(dt) diff --git a/code/rest/install_controller.go b/code/rest/install_controller.go index 5f4b675..02b417f 100644 --- a/code/rest/install_controller.go +++ b/code/rest/install_controller.go @@ -272,8 +272,8 @@ func (this *InstallController) CreateAdmin(writer http.ResponseWriter, request * adminPassword := request.FormValue("adminPassword") //validate admin's username - if m, _ := regexp.MatchString(`^[0-9a-zA-Z_]+$`, adminUsername); !m { - panic(result.BadRequest(`admin's username cannot oly be alphabet, number or '_'`)) + if m, _ := regexp.MatchString(USERNAME_PATTERN, adminUsername); !m { + panic(result.BadRequestI18n(request, i18n.UsernameError)) } if len(adminPassword) < 6 { diff --git a/code/rest/matter_controller.go b/code/rest/matter_controller.go index 9c79e58..4ec8e8b 100644 --- a/code/rest/matter_controller.go +++ b/code/rest/matter_controller.go @@ -142,7 +142,7 @@ func (this *MatterController) Page(writer http.ResponseWriter, request *http.Req user := this.findUser(request) - this.shareService.ValidateMatter(shareUuid, shareCode, user, shareRootUuid, dirMatter) + this.shareService.ValidateMatter(request, shareUuid, shareCode, user, shareRootUuid, dirMatter) userUuid = dirMatter.Uuid } else { diff --git a/code/rest/matter_model.go b/code/rest/matter_model.go index b6067ea..dc6a692 100644 --- a/code/rest/matter_model.go +++ b/code/rest/matter_model.go @@ -3,7 +3,12 @@ package rest import ( "fmt" "github.com/eyebluecn/tank/code/core" + "github.com/eyebluecn/tank/code/tool/i18n" + "github.com/eyebluecn/tank/code/tool/result" "github.com/eyebluecn/tank/code/tool/util" + "net/http" + "regexp" + "strings" ) const ( @@ -89,3 +94,22 @@ func GetUserZipRootDir(username string) (rootDirPath string) { return rootDirPath } + +//check matter's name. If error, panic. +func CheckMatterName(request *http.Request, name string) string { + + if name == "" { + panic(result.BadRequest("name cannot be null")) + } + if strings.HasPrefix(name, " ") || strings.HasSuffix(name, " ") { + panic(result.BadRequest("name cannot start with or end with space")) + } + if m, _ := regexp.MatchString(MATTER_NAME_PATTERN, name); m { + panic(result.BadRequestI18n(request, i18n.MatterNameContainSpecialChars)) + } + + if len(name) > MATTER_NAME_MAX_LENGTH { + panic(result.BadRequestI18n(request, i18n.MatterNameLengthExceedLimit, len(name), MATTER_NAME_MAX_LENGTH)) + } + return name +} diff --git a/code/rest/matter_service.go b/code/rest/matter_service.go index 706c863..124b596 100644 --- a/code/rest/matter_service.go +++ b/code/rest/matter_service.go @@ -12,14 +12,14 @@ import ( "io/ioutil" "net/http" "os" + "path" "regexp" "strings" "time" ) /** - * 操作文件的Service - * 以 Atomic 开头的方法带有操作锁,这种方法不能被其他的Atomic方法调用,只能提供给外部调用。 + * Methods start with Atomic has Lock. These method cannot be invoked by other Atomic Methods. */ //@Service type MatterService struct { @@ -67,7 +67,7 @@ func (this *MatterService) Init() { } -//文件下载。支持分片下载 +//Download. Support chunk download. func (this *MatterService) DownloadFile( writer http.ResponseWriter, request *http.Request, @@ -78,7 +78,7 @@ func (this *MatterService) DownloadFile( download.DownloadFile(writer, request, filePath, filename, withContentDisposition) } -//下载文件 要求必须是同一个父matter下的。 +//Download specified matters. matters must have the same puuid. func (this *MatterService) DownloadZip( writer http.ResponseWriter, request *http.Request, @@ -100,34 +100,31 @@ func (this *MatterService) DownloadZip( preference := this.preferenceService.Fetch() + //count the num of files will be downloaded. var count int64 = 0 for _, matter := range matters { - //验证文件夹中文件数量 count = count + this.matterDao.CountByUserUuidAndPath(matter.UserUuid, matter.Path) } - //文件数量判断。 if preference.DownloadDirMaxNum >= 0 { if count > preference.DownloadDirMaxNum { panic(result.BadRequestI18n(request, i18n.MatterSelectNumExceedLimit, count, preference.DownloadDirMaxNum)) } } + //count the size of files will be downloaded. var sumSize int64 = 0 for _, matter := range matters { - //验证文件夹中文件数量 sumSize = sumSize + this.matterDao.SumSizeByUserUuidAndPath(matter.UserUuid, matter.Path) } - //判断用户自身上传大小的限制 - //大小判断 if preference.DownloadDirMaxSize >= 0 { if sumSize > preference.DownloadDirMaxSize { panic(result.BadRequestI18n(request, i18n.MatterSelectSizeExceedLimit, util.HumanFileSize(sumSize), util.HumanFileSize(preference.DownloadDirMaxSize))) } } - //准备zip放置的目录。 + //prepare the temp zip dir destZipDirPath := fmt.Sprintf("%s/%d", GetUserZipRootDir(matters[0].Username), time.Now().UnixNano()/1e6) util.MakeDirAll(destZipDirPath) @@ -138,13 +135,11 @@ func (this *MatterService) DownloadZip( destZipPath := fmt.Sprintf("%s/%s", destZipDirPath, destZipName) - //_ = util.Zip(matter.AbsolutePath(), destZipPath) this.zipMatters(request, matters, destZipPath) - //下载 download.DownloadFile(writer, request, destZipPath, destZipName, true) - //删除临时压缩文件 + //delete the temp zip file. err := os.Remove(destZipPath) if err != nil { this.logger.Error("error while deleting zip file %s", err.Error()) @@ -153,14 +148,14 @@ func (this *MatterService) DownloadZip( } -//打包一系列的matter +//zip matters. func (this *MatterService) zipMatters(request *http.Request, matters []*Matter, destPath string) { if util.PathExists(destPath) { panic(result.BadRequest("%s exists", destPath)) } - //要求必须是同一个父matter下的。 + //matters must have the same puuid. if matters == nil || len(matters) == 0 { panic(result.BadRequest("matters cannot be nil.")) } @@ -176,12 +171,12 @@ func (this *MatterService) zipMatters(request *http.Request, matters []*Matter, } } - //将每个matter的children装填好 + //wrap children for every matter. for _, m := range matters { this.WrapChildrenDetail(request, m) } - // 创建准备写入的文件 + // create temp zip file. fileWriter, err := os.Create(destPath) this.PanicError(err) @@ -190,15 +185,13 @@ func (this *MatterService) zipMatters(request *http.Request, matters []*Matter, this.PanicError(err) }() - // 通过 fileWriter 来创建 zip.Write zipWriter := zip.NewWriter(fileWriter) defer func() { - // 检测一下是否成功关闭 err := zipWriter.Close() this.PanicError(err) }() - //采用深度优先搜索算法。 + //DFS algorithm var walkFunc func(matter *Matter) walkFunc = func(matter *Matter) { @@ -207,27 +200,24 @@ func (this *MatterService) zipMatters(request *http.Request, matters []*Matter, fileInfo, err := os.Stat(path) this.PanicError(err) - // 通过文件信息,创建 zip 的文件信息 + // Create file info. fileHeader, err := zip.FileInfoHeader(fileInfo) this.PanicError(err) - // 替换文件信息中的文件名 + // Trim the baseDirPath fileHeader.Name = strings.TrimPrefix(path, baseDirPath) - // 目录前要加上/ + // directory has prefix / if matter.Dir { fileHeader.Name += "/" } - // 写入文件信息,并返回一个 Write 结构 writer, err := zipWriter.CreateHeader(fileHeader) this.PanicError(err) - // 检测,如果不是标准文件就只写入头信息,不写入文件数据到 writer - // 如目录,也没有数据需要写 + // only regular file has things to write. if fileHeader.Mode().IsRegular() { - // 打开要压缩的文件 fileToBeZip, err := os.Open(path) defer func() { err = fileToBeZip.Close() @@ -235,13 +225,12 @@ func (this *MatterService) zipMatters(request *http.Request, matters []*Matter, }() this.PanicError(err) - // 将打开的文件 Copy 到 writer _, err = io.Copy(writer, fileToBeZip) this.PanicError(err) } - //如果有子文件,继续往下钻 + //dfs. for _, m := range matter.Children { walkFunc(m) } @@ -252,7 +241,7 @@ func (this *MatterService) zipMatters(request *http.Request, matters []*Matter, } } -//删除文件 +//delete files. func (this *MatterService) Delete(request *http.Request, matter *Matter) { if matter == nil { @@ -261,42 +250,39 @@ func (this *MatterService) Delete(request *http.Request, matter *Matter) { this.matterDao.Delete(matter) - //重新计算文件大小 + //re compute the size of Route. this.matterDao.ComputeRouteSize(matter.Puuid, matter.UserUuid) } -//删除文件 +//atomic delete files func (this *MatterService) AtomicDelete(request *http.Request, matter *Matter) { if matter == nil { panic(result.BadRequest("matter cannot be nil")) } - //操作锁 + //lock this.userService.MatterLock(matter.UserUuid) defer this.userService.MatterUnlock(matter.UserUuid) this.Delete(request, matter) } -//上传文件 +//upload files. func (this *MatterService) Upload(request *http.Request, file io.Reader, user *User, dirMatter *Matter, filename string, privacy bool) *Matter { if user == nil { panic(result.BadRequest("user cannot be nil.")) } - //验证dirMatter if dirMatter == nil { panic(result.BadRequest("dirMatter cannot be nil.")) } - //文件名不能太长。 if len(filename) > MATTER_NAME_MAX_LENGTH { panic(result.BadRequestI18n(request, i18n.MatterNameLengthExceedLimit, len(filename), MATTER_NAME_MAX_LENGTH)) } - //文件夹路径 dirAbsolutePath := dirMatter.AbsolutePath() dirRelativePath := dirMatter.Path @@ -305,17 +291,15 @@ func (this *MatterService) Upload(request *http.Request, file io.Reader, user *U panic(result.BadRequest("%s already exists", filename)) } - //获取文件应该存放在的物理路径的绝对路径和相对路径。 fileAbsolutePath := dirAbsolutePath + "/" + filename fileRelativePath := dirRelativePath + "/" + filename - //创建父文件夹 util.MakeDirAll(dirAbsolutePath) - //如果文件已经存在了,那么直接覆盖。 + //if exist, overwrite it. exist := util.PathExists(fileAbsolutePath) if exist { - this.logger.Error("%s已经存在,将其删除", fileAbsolutePath) + this.logger.Error("%s exits, overwrite it.", fileAbsolutePath) removeError := os.Remove(fileAbsolutePath) this.PanicError(removeError) } @@ -331,20 +315,20 @@ func (this *MatterService) Upload(request *http.Request, file io.Reader, user *U fileSize, err := io.Copy(destFile, file) this.PanicError(err) - this.logger.Info("上传文件 %s 大小为 %v ", filename, util.HumanFileSize(fileSize)) + this.logger.Info("upload %s %v ", filename, util.HumanFileSize(fileSize)) - //判断用户自身上传大小的限制。 + //check the size limit. if user.SizeLimit >= 0 { if fileSize > user.SizeLimit { - //删除上传过来的内容 + //delete the file on disk. err = os.Remove(fileAbsolutePath) this.PanicError(err) - panic(result.BadRequest("file's size exceed the limit %s > %s ", util.HumanFileSize(user.SizeLimit), util.HumanFileSize(fileSize))) + panic(result.BadRequestI18n(request, i18n.MatterSizeExceedLimit, util.HumanFileSize(fileSize), util.HumanFileSize(user.SizeLimit))) } } - //将文件信息存入数据库中。 + //write to db. matter := &Matter{ Puuid: dirMatter.Uuid, UserUuid: user.Uuid, @@ -358,7 +342,7 @@ func (this *MatterService) Upload(request *http.Request, file io.Reader, user *U } matter = this.matterDao.Create(matter) - //统计文件夹的大小。 + //compute the size of directory go core.RunWithRecovery(func() { this.matterDao.ComputeRouteSize(dirMatter.Uuid, user.Uuid) }) @@ -366,15 +350,13 @@ func (this *MatterService) Upload(request *http.Request, file io.Reader, user *U return matter } -//内部创建文件,不带操作锁。 +//inner create directory. func (this *MatterService) createDirectory(request *http.Request, dirMatter *Matter, name string, user *User) *Matter { - //父级matter必须存在 if dirMatter == nil { panic(result.BadRequest("dirMatter cannot be nil")) } - //必须是文件夹 if !dirMatter.Dir { panic(result.BadRequest("dirMatter must be directory")) } @@ -385,7 +367,6 @@ func (this *MatterService) createDirectory(request *http.Request, dirMatter *Mat } name = strings.TrimSpace(name) - //验证参数。 if name == "" { panic(result.BadRequest("name cannot be blank")) } @@ -400,7 +381,7 @@ func (this *MatterService) createDirectory(request *http.Request, dirMatter *Mat panic(result.BadRequestI18n(request, i18n.MatterNameContainSpecialChars)) } - //判断同级文件夹中是否有同名的文件夹。存在了直接返回即可。 + //if exist. return. matter := this.matterDao.FindByUserUuidAndPuuidAndDirAndName(user.Uuid, dirMatter.Uuid, true, name) if matter != nil { return matter @@ -412,17 +393,15 @@ func (this *MatterService) createDirectory(request *http.Request, dirMatter *Mat panic(result.BadRequestI18n(request, i18n.MatterDepthExceedLimit, len(parts), MATTER_NAME_MAX_DEPTH)) } - //绝对路径 absolutePath := GetUserMatterRootDir(user.Username) + dirMatter.Path + "/" + name - //相对路径 relativePath := dirMatter.Path + "/" + name - //磁盘中创建文件夹。 + //crate directory on disk. dirPath := util.MakeDirAll(absolutePath) this.logger.Info("Create Directory: %s", dirPath) - //数据库中创建文件夹。 + //create in db matter = &Matter{ Puuid: dirMatter.Uuid, UserUuid: user.Uuid, @@ -437,10 +416,8 @@ func (this *MatterService) createDirectory(request *http.Request, dirMatter *Mat return matter } -//在dirMatter中创建文件夹 返回刚刚创建的这个文件夹 func (this *MatterService) AtomicCreateDirectory(request *http.Request, dirMatter *Matter, name string, user *User) *Matter { - //操作锁 this.userService.MatterLock(user.Uuid) defer this.userService.MatterUnlock(user.Uuid) @@ -449,16 +426,14 @@ func (this *MatterService) AtomicCreateDirectory(request *http.Request, dirMatte return matter } -//处理 移动和复制时可能存在的覆盖问题。 +//copy or move may overwrite. func (this *MatterService) handleOverwrite(request *http.Request, userUuid string, destinationPath string, overwrite bool) { - //目标matter。因为有可能已经存在了 destMatter := this.matterDao.findByUserUuidAndPath(userUuid, destinationPath) - //如果目标matter存在了。 if destMatter != nil { - //如果目标matter还存在了。 + //if exist if overwrite { - //要求覆盖。那么删除。 + //delete. this.Delete(request, destMatter) } else { panic(result.BadRequestI18n(request, i18n.MatterExist, destMatter.Path)) @@ -467,7 +442,7 @@ func (this *MatterService) handleOverwrite(request *http.Request, userUuid strin } -//将一个srcMatter放置到另一个destMatter(必须为文件夹)下 不关注 overwrite 和 lock. +//move srcMatter to destMatter. invoker must handled the overwrite and lock. func (this *MatterService) move(request *http.Request, srcMatter *Matter, destDirMatter *Matter) { if srcMatter == nil { @@ -483,59 +458,59 @@ func (this *MatterService) move(request *http.Request, srcMatter *Matter, destDi destDirUuid := destDirMatter.Uuid if srcMatter.Dir { - //如果源是文件夹 + + //if src is dir. destAbsolutePath := destDirMatter.AbsolutePath() + "/" + srcMatter.Name srcAbsolutePath := srcMatter.AbsolutePath() - //物理文件一口气移动 + //move src to dest on disk. err := os.Rename(srcAbsolutePath, destAbsolutePath) this.PanicError(err) - //修改数据库中信息 + //change info on db. srcMatter.Puuid = destDirMatter.Uuid srcMatter.Path = destDirMatter.Path + "/" + srcMatter.Name srcMatter = this.matterDao.Save(srcMatter) - //调整该文件夹下文件的Path. + //reCompute the path. matters := this.matterDao.ListByPuuidAndUserUuid(srcMatter.Uuid, srcMatter.UserUuid, nil) for _, m := range matters { this.adjustPath(m, srcMatter) } } else { - //如果源是普通文件 + //if src is NOT dir. destAbsolutePath := destDirMatter.AbsolutePath() + "/" + srcMatter.Name srcAbsolutePath := srcMatter.AbsolutePath() - //物理文件进行移动 + //move src to dest on disk. err := os.Rename(srcAbsolutePath, destAbsolutePath) this.PanicError(err) - //删除对应的缓存。 + //delete caches. this.imageCacheDao.DeleteByMatterUuid(srcMatter.Uuid) - //修改数据库中信息 + //change info in db. srcMatter.Puuid = destDirMatter.Uuid srcMatter.Path = destDirMatter.Path + "/" + srcMatter.Name srcMatter = this.matterDao.Save(srcMatter) } - //调整两个文件夹的大小 + //reCompute the size of src and dest. this.matterDao.ComputeRouteSize(srcPuuid, userUuid) this.matterDao.ComputeRouteSize(destDirUuid, userUuid) } -//将一个srcMatter放置到另一个destMatter(必须为文件夹)下 +//move srcMatter to destMatter(must be dir) func (this *MatterService) AtomicMove(request *http.Request, srcMatter *Matter, destDirMatter *Matter, overwrite bool) { if srcMatter == nil { panic(result.BadRequest("srcMatter cannot be nil.")) } - //操作锁 this.userService.MatterLock(srcMatter.UserUuid) defer this.userService.MatterUnlock(srcMatter.UserUuid) @@ -546,7 +521,7 @@ func (this *MatterService) AtomicMove(request *http.Request, srcMatter *Matter, panic(result.BadRequestI18n(request, i18n.MatterDestinationMustDirectory)) } - //文件夹不能把自己移入到自己中,也不可以移入到自己的子文件夹下。 + //neither move to itself, nor move to its children. destDirMatter = this.WrapParentDetail(request, destDirMatter) tmpMatter := destDirMatter for tmpMatter != nil { @@ -556,22 +531,21 @@ func (this *MatterService) AtomicMove(request *http.Request, srcMatter *Matter, tmpMatter = tmpMatter.Parent } - //处理覆盖的问题 + //handle the overwrite destinationPath := destDirMatter.Path + "/" + srcMatter.Name this.handleOverwrite(request, srcMatter.UserUuid, destinationPath, overwrite) - //做move操作。 + //do the move operation. this.move(request, srcMatter, destDirMatter) } -//将一个srcMatter放置到另一个destMatter(必须为文件夹)下 +//move srcMatters to destMatter(must be dir) func (this *MatterService) AtomicMoveBatch(request *http.Request, srcMatters []*Matter, destDirMatter *Matter) { if destDirMatter == nil { panic(result.BadRequest("destDirMatter cannot be nil.")) } - //操作锁 this.userService.MatterLock(destDirMatter.UserUuid) defer this.userService.MatterUnlock(destDirMatter.UserUuid) @@ -583,14 +557,14 @@ func (this *MatterService) AtomicMoveBatch(request *http.Request, srcMatters []* panic(result.BadRequestI18n(request, i18n.MatterDestinationMustDirectory)) } - //文件夹不能把自己移入到自己中,也不可以移入到自己的子文件夹下。 + //neither move to itself, nor move to its children. destDirMatter = this.WrapParentDetail(request, destDirMatter) for _, srcMatter := range srcMatters { tmpMatter := destDirMatter for tmpMatter != nil { if srcMatter.Uuid == tmpMatter.Uuid { - panic("文件夹不能把自己移入到自己中,也不可以移入到自己的子文件夹下。") + panic(result.BadRequestI18n(request, i18n.MatterMoveRecursive)) } tmpMatter = tmpMatter.Parent } @@ -602,14 +576,11 @@ func (this *MatterService) AtomicMoveBatch(request *http.Request, srcMatters []* } -//内部移动一个文件(提供给Copy调用),无需关心overwrite问题。 +//copy srcMatter to destMatter. invoker must handled the overwrite and lock. func (this *MatterService) copy(request *http.Request, srcMatter *Matter, destDirMatter *Matter, name string) { if srcMatter.Dir { - //如果源是文件夹 - - //在目标地址创建新文件夹。 newMatter := &Matter{ Puuid: destDirMatter.Uuid, UserUuid: srcMatter.UserUuid, @@ -624,7 +595,7 @@ func (this *MatterService) copy(request *http.Request, srcMatter *Matter, destDi newMatter = this.matterDao.Create(newMatter) - //复制子文件或文件夹 + //copy children matters := this.matterDao.ListByPuuidAndUserUuid(srcMatter.Uuid, srcMatter.UserUuid, nil) for _, m := range matters { this.copy(request, m, newMatter, m.Name) @@ -632,14 +603,12 @@ func (this *MatterService) copy(request *http.Request, srcMatter *Matter, destDi } else { - //如果源是普通文件 destAbsolutePath := destDirMatter.AbsolutePath() + "/" + name srcAbsolutePath := srcMatter.AbsolutePath() - //物理文件进行复制 + //copy file on disk. util.CopyFile(srcAbsolutePath, destAbsolutePath) - //创建新文件的数据库信息。 newMatter := &Matter{ Puuid: destDirMatter.Uuid, UserUuid: srcMatter.UserUuid, @@ -656,14 +625,13 @@ func (this *MatterService) copy(request *http.Request, srcMatter *Matter, destDi } } -//将一个srcMatter复制到另一个destMatter(必须为文件夹)下,名字叫做name +//copy srcMatter to destMatter. func (this *MatterService) AtomicCopy(request *http.Request, srcMatter *Matter, destDirMatter *Matter, name string, overwrite bool) { if srcMatter == nil { panic(result.BadRequest("srcMatter cannot be nil.")) } - //操作锁 this.userService.MatterLock(srcMatter.UserUuid) defer this.userService.MatterUnlock(srcMatter.UserUuid) @@ -677,29 +645,17 @@ func (this *MatterService) AtomicCopy(request *http.Request, srcMatter *Matter, this.copy(request, srcMatter, destDirMatter, name) } -//将一个matter 重命名为 name +//rename matter to name func (this *MatterService) AtomicRename(request *http.Request, matter *Matter, name string, user *User) { if user == nil { panic(result.BadRequest("user cannot be nil")) } - //操作锁 this.userService.MatterLock(user.Uuid) defer this.userService.MatterUnlock(user.Uuid) - //验证参数。 - if name == "" { - panic(result.BadRequest("name cannot be null")) - } - if m, _ := regexp.MatchString(MATTER_NAME_PATTERN, name); m { - panic(result.BadRequestI18n(request, i18n.MatterNameContainSpecialChars)) - } - - //文件名不能太长。 - if len(name) > MATTER_NAME_MAX_LENGTH { - panic(result.BadRequestI18n(request, i18n.MatterNameLengthExceedLimit, len(name), MATTER_NAME_MAX_LENGTH)) - } + name = CheckMatterName(request, name) if name == matter.Name { panic(result.BadRequestI18n(request, i18n.MatterNameNoChange)) @@ -853,36 +809,33 @@ func (this *MatterService) mirror(request *http.Request, srcPath string, destDir //根据一个文件夹路径,依次创建,找到最后一个文件夹的matter,如果中途出错,返回err. 如果存在了那就直接返回即可。 func (this *MatterService) CreateDirectories(request *http.Request, user *User, dirPath string) *Matter { - if dirPath == "" { - panic(`文件夹不能为空`) - } else if dirPath[0:1] != "/" { - panic(`文件夹必须以/开头`) - } else if strings.Index(dirPath, "//") != -1 { - panic(`文件夹不能出现连续的//`) - } else if m, _ := regexp.MatchString(MATTER_NAME_PATTERN, dirPath); m { - panic(result.BadRequestI18n(request, i18n.MatterNameContainSpecialChars)) - } + dirPath = path.Clean(dirPath) if dirPath == "/" { return NewRootMatter(user) } - //如果最后一个符号为/自动忽略 - if dirPath[len(dirPath)-1] == '/' { - dirPath = dirPath[:len(dirPath)-1] - } + //ignore the last slash. + dirPath = strings.TrimSuffix(dirPath, "/") - //递归找寻文件的上级目录uuid. folders := strings.Split(dirPath, "/") if len(folders) > MATTER_NAME_MAX_DEPTH { panic(result.BadRequestI18n(request, i18n.MatterDepthExceedLimit, len(folders), MATTER_NAME_MAX_DEPTH)) } + //validate every matter name. + for k, name := range folders { + //first element is "" + if k != 0 { + CheckMatterName(request, name) + } + } + var dirMatter *Matter for k, name := range folders { - //split的第一个元素为空字符串,忽略。 + //ignore the first element. if k == 0 { dirMatter = NewRootMatter(user) continue @@ -894,14 +847,13 @@ func (this *MatterService) CreateDirectories(request *http.Request, user *User, return dirMatter } -//包装某个matter的详情。会把父级依次倒着装进去。如果中途出错,直接抛出异常。 +//wrap a matter. put its parent. func (this *MatterService) WrapParentDetail(request *http.Request, matter *Matter) *Matter { if matter == nil { panic(result.BadRequest("matter cannot be nil.")) } - //组装file的内容,展示其父组件。 puuid := matter.Puuid tmpMatter := matter for puuid != MATTER_ROOT { @@ -914,7 +866,7 @@ func (this *MatterService) WrapParentDetail(request *http.Request, matter *Matte return matter } -//包装某个matter的详情。会把子级依次装进去。如果中途出错,直接抛出异常。 +//wrap a matter ,put its children func (this *MatterService) WrapChildrenDetail(request *http.Request, matter *Matter) { if matter == nil { @@ -933,20 +885,19 @@ func (this *MatterService) WrapChildrenDetail(request *http.Request, matter *Mat } -//获取某个文件的详情,会把父级依次倒着装进去。如果中途出错,直接抛出异常。 +//fetch a matter's detail with parent info. func (this *MatterService) Detail(request *http.Request, uuid string) *Matter { matter := this.matterDao.CheckByUuid(uuid) return this.WrapParentDetail(request, matter) } -//去指定的url中爬文件 +//crawl a url to dirMatter func (this *MatterService) AtomicCrawl(request *http.Request, url string, filename string, user *User, dirMatter *Matter, privacy bool) *Matter { if user == nil { panic(result.BadRequest("user cannot be nil.")) } - //操作锁 this.userService.MatterLock(user.Uuid) defer this.userService.MatterUnlock(user.Uuid) @@ -958,36 +909,31 @@ func (this *MatterService) AtomicCrawl(request *http.Request, url string, filena panic(result.BadRequest("filename cannot be null.")) } - //从指定的url下载一个文件。参考:https://golangcode.com/download-a-file-from-a-url/ + //download from url. resp, err := http.Get(url) this.PanicError(err) return this.Upload(request, resp.Body, user, dirMatter, filename, privacy) } -//调整一个Matter的path值 +//adjust a matter's path. func (this *MatterService) adjustPath(matter *Matter, parentMatter *Matter) { if matter.Dir { - //如果源是文件夹 - //首先调整好自己 matter.Path = parentMatter.Path + "/" + matter.Name matter = this.matterDao.Save(matter) - //调整该文件夹下文件的Path. + //adjust children. matters := this.matterDao.ListByPuuidAndUserUuid(matter.Uuid, matter.UserUuid, nil) for _, m := range matters { this.adjustPath(m, matter) } } else { - //如果源是普通文件 - - //删除该文件的所有缓存 + //delete caches. this.imageCacheDao.DeleteByMatterUuid(matter.Uuid) - //调整path matter.Path = parentMatter.Path + "/" + matter.Name matter = this.matterDao.Save(matter) } diff --git a/code/rest/preference_controller.go b/code/rest/preference_controller.go index 3656af0..598ff50 100644 --- a/code/rest/preference_controller.go +++ b/code/rest/preference_controller.go @@ -41,14 +41,13 @@ func (this *PreferenceController) RegisterRoutes() map[string]func(writer http.R return routeMap } -//简单验证蓝眼云盘服务是否已经启动了。 +//ping the application. Return current version. func (this *PreferenceController) Ping(writer http.ResponseWriter, request *http.Request) *result.WebResult { - return this.Success(nil) + return this.Success(core.VERSION) } -//查看某个偏好设置的详情。 func (this *PreferenceController) Fetch(writer http.ResponseWriter, request *http.Request) *result.WebResult { preference := this.preferenceService.Fetch() @@ -56,14 +55,9 @@ func (this *PreferenceController) Fetch(writer http.ResponseWriter, request *htt return this.Success(preference) } -//修改 func (this *PreferenceController) Edit(writer http.ResponseWriter, request *http.Request) *result.WebResult { - //验证参数。 name := request.FormValue("name") - if name == "" { - panic("name参数必填") - } logoUrl := request.FormValue("logoUrl") faviconUrl := request.FormValue("faviconUrl") @@ -74,9 +68,13 @@ func (this *PreferenceController) Edit(writer http.ResponseWriter, request *http defaultTotalSizeLimitStr := request.FormValue("defaultTotalSizeLimit") allowRegisterStr := request.FormValue("allowRegister") + if name == "" { + panic(result.BadRequest("name cannot be null")) + } + var downloadDirMaxSize int64 = 0 if downloadDirMaxSizeStr == "" { - panic("文件下载大小限制必填!") + panic(result.BadRequest("downloadDirMaxSize cannot be null")) } else { intDownloadDirMaxSize, err := strconv.Atoi(downloadDirMaxSizeStr) this.PanicError(err) @@ -85,7 +83,7 @@ func (this *PreferenceController) Edit(writer http.ResponseWriter, request *http var downloadDirMaxNum int64 = 0 if downloadDirMaxNumStr == "" { - panic("文件下载数量限制必填!") + panic(result.BadRequest("downloadDirMaxNum cannot be null")) } else { intDownloadDirMaxNum, err := strconv.Atoi(downloadDirMaxNumStr) this.PanicError(err) @@ -94,7 +92,7 @@ func (this *PreferenceController) Edit(writer http.ResponseWriter, request *http var defaultTotalSizeLimit int64 = 0 if defaultTotalSizeLimitStr == "" { - panic("用户默认总限制!") + panic(result.BadRequest("defaultTotalSizeLimit cannot be null")) } else { intDefaultTotalSizeLimit, err := strconv.Atoi(defaultTotalSizeLimitStr) this.PanicError(err) @@ -119,13 +117,13 @@ func (this *PreferenceController) Edit(writer http.ResponseWriter, request *http preference = this.preferenceDao.Save(preference) - //重置缓存中的偏好 + //reset the preference cache this.preferenceService.Reset() return this.Success(preference) } -//清扫系统,所有数据全部丢失。一定要非常慎点,非常慎点!只在系统初始化的时候点击! +//cleanup system data. func (this *PreferenceController) SystemCleanup(writer http.ResponseWriter, request *http.Request) *result.WebResult { user := this.checkUser(request) @@ -135,7 +133,7 @@ func (this *PreferenceController) SystemCleanup(writer http.ResponseWriter, requ panic(result.BadRequest("password error")) } - //清空系统 + //this will trigger every bean to cleanup. core.CONTEXT.Cleanup() return this.Success("OK") diff --git a/code/rest/preference_dao.go b/code/rest/preference_dao.go index 8049853..f9eba04 100644 --- a/code/rest/preference_dao.go +++ b/code/rest/preference_dao.go @@ -33,7 +33,6 @@ func (this *PreferenceDao) Fetch() *Preference { return preference } -//创建 func (this *PreferenceDao) Create(preference *Preference) *Preference { timeUUID, _ := uuid.NewV4() @@ -47,7 +46,6 @@ func (this *PreferenceDao) Create(preference *Preference) *Preference { return preference } -//修改一个偏好设置 func (this *PreferenceDao) Save(preference *Preference) *Preference { preference.UpdateTime = time.Now() diff --git a/code/rest/preference_service.go b/code/rest/preference_service.go index 2180c88..0bb4e66 100644 --- a/code/rest/preference_service.go +++ b/code/rest/preference_service.go @@ -19,7 +19,6 @@ func (this *PreferenceService) Init() { } -//获取单例的配置。 func (this *PreferenceService) Fetch() *Preference { if this.preference == nil { diff --git a/code/rest/session_dao.go b/code/rest/session_dao.go index 6df3f0a..d45ceb3 100644 --- a/code/rest/session_dao.go +++ b/code/rest/session_dao.go @@ -34,7 +34,6 @@ func (this *SessionDao) CheckByUuid(uuid string) *Session { return entity } -//创建一个session并且持久化到数据库中。 func (this *SessionDao) Create(session *Session) *Session { timeUUID, _ := uuid.NewV4() @@ -48,7 +47,6 @@ func (this *SessionDao) Create(session *Session) *Session { return session } -//修改一个session func (this *SessionDao) Save(session *Session) *Session { session.UpdateTime = time.Now() diff --git a/code/rest/share_controller.go b/code/rest/share_controller.go index 9894ef1..84f4ea9 100644 --- a/code/rest/share_controller.go +++ b/code/rest/share_controller.go @@ -66,7 +66,6 @@ func (this *ShareController) RegisterRoutes() map[string]func(writer http.Respon return routeMap } -//创建一个分享 func (this *ShareController) Create(writer http.ResponseWriter, request *http.Request) *result.WebResult { matterUuids := request.FormValue("matterUuids") @@ -113,9 +112,8 @@ func (this *ShareController) Create(writer http.ResponseWriter, request *http.Re matter := this.matterDao.CheckByUuid(uuid) - //判断文件的所属人是否正确 if matter.UserUuid != user.Uuid { - panic(result.Unauthorized("不是你的文件,没有权限")) + panic(result.UNAUTHORIZED) } matters = append(matters, matter) @@ -130,7 +128,7 @@ func (this *ShareController) Create(writer http.ResponseWriter, request *http.Re } } else { if matter.Puuid != puuid { - panic(result.Unauthorized("一次只能分享同一个文件夹中的内容")) + panic(result.Unauthorized("you can only share files in the same directory")) } } @@ -138,10 +136,9 @@ func (this *ShareController) Create(writer http.ResponseWriter, request *http.Re if len(matters) > 1 { shareType = SHARE_TYPE_MIX - name = matters[0].Name + "," + matters[1].Name + " 等" + name = matters[0].Name + "," + matters[1].Name + " ..." } - //创建share记录 share := &Share{ Name: name, ShareType: shareType, @@ -154,7 +151,6 @@ func (this *ShareController) Create(writer http.ResponseWriter, request *http.Re } this.shareDao.Create(share) - //创建关联的matter for _, matter := range matters { bridge := &Bridge{ ShareUuid: share.Uuid, @@ -177,7 +173,6 @@ func (this *ShareController) Delete(writer http.ResponseWriter, request *http.Re if share != nil { - //删除对应的bridge. this.bridgeDao.DeleteByShareUuid(share.Uuid) this.shareDao.Delete(share) @@ -186,7 +181,6 @@ func (this *ShareController) Delete(writer http.ResponseWriter, request *http.Re return this.Success(nil) } -//删除一系列分享 func (this *ShareController) DeleteBatch(writer http.ResponseWriter, request *http.Request) *result.WebResult { uuids := request.FormValue("uuids") @@ -200,7 +194,6 @@ func (this *ShareController) DeleteBatch(writer http.ResponseWriter, request *ht imageCache := this.shareDao.FindByUuid(uuid) - //判断图片缓存的所属人是否正确 user := this.checkUser(request) if imageCache.UserUuid != user.Uuid { panic(result.UNAUTHORIZED) @@ -209,10 +202,9 @@ func (this *ShareController) DeleteBatch(writer http.ResponseWriter, request *ht this.shareDao.Delete(imageCache) } - return this.Success("删除成功!") + return this.Success("OK") } -//查看详情。 func (this *ShareController) Detail(writer http.ResponseWriter, request *http.Request) *result.WebResult { uuid := request.FormValue("uuid") @@ -222,7 +214,6 @@ func (this *ShareController) Detail(writer http.ResponseWriter, request *http.Re share := this.shareDao.CheckByUuid(uuid) - //验证当前之人是否有权限查看这么详细。 user := this.checkUser(request) if share.UserUuid != user.Uuid { @@ -233,10 +224,8 @@ func (this *ShareController) Detail(writer http.ResponseWriter, request *http.Re } -//按照分页的方式查询 func (this *ShareController) Page(writer http.ResponseWriter, request *http.Request) *result.WebResult { - //如果是根目录,那么就传入root. pageStr := request.FormValue("page") pageSizeStr := request.FormValue("pageSize") orderCreateTime := request.FormValue("orderCreateTime") @@ -268,37 +257,30 @@ func (this *ShareController) Page(writer http.ResponseWriter, request *http.Requ return this.Success(pager) } -//验证提取码对应的某个shareUuid是否有效 func (this *ShareController) CheckShare(writer http.ResponseWriter, request *http.Request) *Share { - //如果是根目录,那么就传入root. shareUuid := request.FormValue("shareUuid") code := request.FormValue("code") user := this.findUser(request) - return this.shareService.CheckShare(shareUuid, code, user) + return this.shareService.CheckShare(request, shareUuid, code, user) } -//浏览某个分享中的文件 func (this *ShareController) Browse(writer http.ResponseWriter, request *http.Request) *result.WebResult { - //如果是根目录,那么就传入root. shareUuid := request.FormValue("shareUuid") code := request.FormValue("code") - //当前查看的puuid。 puuid=root表示查看分享的根目录,其余表示查看某个文件夹下的文件。 + //puuid can be "root" puuid := request.FormValue("puuid") rootUuid := request.FormValue("rootUuid") user := this.findUser(request) - share := this.shareService.CheckShare(shareUuid, code, user) + share := this.shareService.CheckShare(request, shareUuid, code, user) bridges := this.bridgeDao.FindByShareUuid(share.Uuid) if puuid == MATTER_ROOT { - //分享的根目录 - - //获取对应的 matter. var matters []*Matter if len(bridges) != 0 { uuids := make([]string, 0) @@ -318,21 +300,21 @@ func (this *ShareController) Browse(writer http.ResponseWriter, request *http.Re } else { - //如果当前查看的目录就是根目录,那么无需再验证 + //if root. No need to validate. if puuid == rootUuid { dirMatter := this.matterDao.CheckByUuid(puuid) share.DirMatter = dirMatter } else { dirMatter := this.matterService.Detail(request, puuid) - //验证 shareRootMatter是否在被分享。 + //check whether shareRootMatter is being sharing shareRootMatter := this.matterDao.CheckByUuid(rootUuid) if !shareRootMatter.Dir { panic(result.BadRequestI18n(request, i18n.MatterDestinationMustDirectory)) } this.bridgeDao.CheckByShareUuidAndMatterUuid(share.Uuid, shareRootMatter.Uuid) - //到rootUuid的地方掐断。 + //stop at rootUuid find := false parentMatter := dirMatter.Parent for parentMatter != nil { @@ -357,14 +339,11 @@ func (this *ShareController) Browse(writer http.ResponseWriter, request *http.Re } -//下载压缩包 func (this *ShareController) Zip(writer http.ResponseWriter, request *http.Request) *result.WebResult { - //如果是根目录,那么就传入root. shareUuid := request.FormValue("shareUuid") code := request.FormValue("code") - //当前查看的puuid。 puuid=root表示查看分享的根目录,其余表示查看某个文件夹下的文件。 puuid := request.FormValue("puuid") rootUuid := request.FormValue("rootUuid") @@ -372,8 +351,8 @@ func (this *ShareController) Zip(writer http.ResponseWriter, request *http.Reque if puuid == MATTER_ROOT { - //下载分享全部内容。 - share := this.shareService.CheckShare(shareUuid, code, user) + //download all things. + share := this.shareService.CheckShare(request, shareUuid, code, user) bridges := this.bridgeDao.FindByShareUuid(share.Uuid) var matterUuids []string for _, bridge := range bridges { @@ -384,9 +363,9 @@ func (this *ShareController) Zip(writer http.ResponseWriter, request *http.Reque } else { - //下载某个目录。 + //download a folder. matter := this.matterDao.CheckByUuid(puuid) - this.shareService.ValidateMatter(shareUuid, code, user, rootUuid, matter) + this.shareService.ValidateMatter(request, shareUuid, code, user, rootUuid, matter) this.matterService.DownloadZip(writer, request, []*Matter{matter}) } diff --git a/code/rest/share_dao.go b/code/rest/share_dao.go index f783c9a..c14297c 100644 --- a/code/rest/share_dao.go +++ b/code/rest/share_dao.go @@ -37,7 +37,6 @@ func (this *ShareDao) CheckByUuid(uuid string) *Share { return entity } -//按分页条件获取分页 func (this *ShareDao) Page(page int, pageSize int, userUuid string, sortArray []builder.OrderPair) *Pager { var wp = &builder.WherePair{} @@ -61,7 +60,6 @@ func (this *ShareDao) Page(page int, pageSize int, userUuid string, sortArray [] return pager } -//创建 func (this *ShareDao) Create(share *Share) *Share { timeUUID, _ := uuid.NewV4() @@ -75,7 +73,6 @@ func (this *ShareDao) Create(share *Share) *Share { return share } -//修改一条记录 func (this *ShareDao) Save(share *Share) *Share { share.UpdateTime = time.Now() diff --git a/code/rest/share_model.go b/code/rest/share_model.go index 961449c..1362136 100644 --- a/code/rest/share_model.go +++ b/code/rest/share_model.go @@ -6,11 +6,11 @@ import ( ) const ( - //单文件 + //single file. SHARE_TYPE_FILE = "FILE" - //文件夹 + //directory SHARE_TYPE_DIRECTORY = "DIRECTORY" - //混合体 + //mix things SHARE_TYPE_MIX = "MIX" ) @@ -19,7 +19,7 @@ const ( ) /** - * 分享记录 + * share record */ type Share struct { Base diff --git a/code/rest/share_service.go b/code/rest/share_service.go index d9007ef..fbb70c2 100644 --- a/code/rest/share_service.go +++ b/code/rest/share_service.go @@ -2,7 +2,9 @@ package rest import ( "github.com/eyebluecn/tank/code/core" + "github.com/eyebluecn/tank/code/tool/i18n" "github.com/eyebluecn/tank/code/tool/result" + "net/http" "strings" "time" ) @@ -41,7 +43,6 @@ func (this *ShareService) Init() { } -//获取某个分享的详情。 func (this *ShareService) Detail(uuid string) *Share { share := this.shareDao.CheckByUuid(uuid) @@ -49,17 +50,17 @@ func (this *ShareService) Detail(uuid string) *Share { return share } -//验证一个shareUuid和shareCode是否匹配和有权限。 -func (this *ShareService) CheckShare(shareUuid string, code string, user *User) *Share { +// check whether shareUuid and shareCode matches. check whether user can access. +func (this *ShareService) CheckShare(request *http.Request, shareUuid string, code string, user *User) *Share { share := this.shareDao.CheckByUuid(shareUuid) - //如果是自己的分享,可以不要提取码 + //if self, not need shareCode if user == nil || user.Uuid != share.UserUuid { - //没有登录,或者查看的不是自己的分享,要求有验证码 + //if not login or not self's share, shareCode is required. if code == "" { - panic(result.CustomWebResult(result.NEED_SHARE_CODE, "提取码必填")) + panic(result.CustomWebResultI18n(request, result.NEED_SHARE_CODE, i18n.ShareCodeRequired)) } else if share.Code != code { - panic(result.CustomWebResult(result.SHARE_CODE_ERROR, "提取码错误")) + panic(result.CustomWebResultI18n(request, result.SHARE_CODE_ERROR, i18n.ShareCodeError)) } else { if !share.ExpireInfinity { if share.ExpireTime.Before(time.Now()) { @@ -71,15 +72,14 @@ func (this *ShareService) CheckShare(shareUuid string, code string, user *User) return share } -//根据某个shareUuid和code,某个用户是否有权限获取 shareRootUuid 下面的 matterUuid -//如果是根目录下的文件,那么shareRootUuid传root. -func (this *ShareService) ValidateMatter(shareUuid string, code string, user *User, shareRootUuid string, matter *Matter) { +//check whether a user can access a matter. shareRootUuid is matter's parent(or parent's parent and so on) +func (this *ShareService) ValidateMatter(request *http.Request, shareUuid string, code string, user *User, shareRootUuid string, matter *Matter) { if matter == nil { panic(result.Unauthorized("matter cannot be nil")) } - //如果文件是自己的,那么放行 + //if self's matter, ok. if user != nil && matter.UserUuid == user.Uuid { return } @@ -88,19 +88,19 @@ func (this *ShareService) ValidateMatter(shareUuid string, code string, user *Us panic(result.Unauthorized("shareUuid,code,shareRootUuid cannot be null")) } - share := this.CheckShare(shareUuid, code, user) + share := this.CheckShare(request, shareUuid, code, user) - //如果shareRootUuid是根,那么matterUuid在bridge中应该有记录 + //if shareRootUuid is root. Bridge must has record. if shareRootUuid == MATTER_ROOT { this.bridgeDao.CheckByShareUuidAndMatterUuid(share.Uuid, matter.Uuid) } else { - //验证 shareRootMatter是否在被分享。 + //check whether shareRootMatter is being sharing shareRootMatter := this.matterDao.CheckByUuid(shareRootUuid) this.bridgeDao.CheckByShareUuidAndMatterUuid(share.Uuid, shareRootMatter.Uuid) - //保证 puuid对应的matter是shareRootMatter的子文件夹。 + // shareRootMatter is ancestor of matter. child := strings.HasPrefix(matter.Path, shareRootMatter.Path) if !child { panic(result.BadRequest("%s is not %s's children", matter.Uuid, shareRootUuid)) diff --git a/code/rest/upload_token_dao.go b/code/rest/upload_token_dao.go index 1cd6dd6..144300d 100644 --- a/code/rest/upload_token_dao.go +++ b/code/rest/upload_token_dao.go @@ -34,7 +34,6 @@ func (this *UploadTokenDao) CheckByUuid(uuid string) *UploadToken { return entity } -//创建一个session并且持久化到数据库中。 func (this *UploadTokenDao) Create(uploadToken *UploadToken) *UploadToken { timeUUID, _ := uuid.NewV4() @@ -49,7 +48,6 @@ func (this *UploadTokenDao) Create(uploadToken *UploadToken) *UploadToken { return uploadToken } -//修改一个uploadToken func (this *UploadTokenDao) Save(uploadToken *UploadToken) *UploadToken { uploadToken.UpdateTime = time.Now() diff --git a/code/rest/user_controller.go b/code/rest/user_controller.go index f18f36c..95ff279 100644 --- a/code/rest/user_controller.go +++ b/code/rest/user_controller.go @@ -47,11 +47,11 @@ func (this *UserController) RegisterRoutes() map[string]func(writer http.Respons func (this *UserController) innerLogin(writer http.ResponseWriter, request *http.Request, user *User) { - //登录成功,设置Cookie。有效期30天。 + //set cookie. expire after 30 days. expiration := time.Now() expiration = expiration.AddDate(0, 0, 30) - //持久化用户的session. + //save session to db. session := &Session{ UserUuid: user.Uuid, Ip: util.GetIpAddress(request), @@ -61,7 +61,7 @@ func (this *UserController) innerLogin(writer http.ResponseWriter, request *http session.CreateTime = time.Now() session = this.sessionDao.Create(session) - //设置用户的cookie. + //set cookie cookie := http.Cookie{ Name: core.COOKIE_AUTH_KEY, Path: "/", @@ -69,16 +69,13 @@ func (this *UserController) innerLogin(writer http.ResponseWriter, request *http Expires: expiration} http.SetCookie(writer, &cookie) - //更新用户上次登录时间和ip + //update lastTime and lastIp user.LastTime = time.Now() user.LastIp = util.GetIpAddress(request) this.userDao.Save(user) } -//使用用户名和密码进行登录。 -//参数: -// @username:用户名 -// @password:密码 +//login by username and password func (this *UserController) Login(writer http.ResponseWriter, request *http.Request) *result.WebResult { username := request.FormValue("username") @@ -101,7 +98,7 @@ func (this *UserController) Login(writer http.ResponseWriter, request *http.Requ return this.Success(user) } -//使用Authentication进行登录。 +//login by authentication. func (this *UserController) AuthenticationLogin(writer http.ResponseWriter, request *http.Request) *result.WebResult { authentication := request.FormValue("authentication") @@ -122,7 +119,7 @@ func (this *UserController) AuthenticationLogin(writer http.ResponseWriter, requ return this.Success(user) } -//用户自主注册。 +//register by username and password. After registering, will auto login. func (this *UserController) Register(writer http.ResponseWriter, request *http.Request) *result.WebResult { username := request.FormValue("username") @@ -133,7 +130,7 @@ func (this *UserController) Register(writer http.ResponseWriter, request *http.R panic(result.BadRequestI18n(request, i18n.UserRegisterNotAllowd)) } - if m, _ := regexp.MatchString(`^[0-9a-zA-Z_]+$`, username); !m { + if m, _ := regexp.MatchString(USERNAME_PATTERN, username); !m { panic(result.BadRequestI18n(request, i18n.UsernameError)) } @@ -141,7 +138,6 @@ func (this *UserController) Register(writer http.ResponseWriter, request *http.R panic(result.BadRequestI18n(request, i18n.UserPasswordLengthError)) } - //判断重名。 if this.userDao.CountByUsername(username) > 0 { panic(result.BadRequestI18n(request, i18n.UsernameExist, username)) } @@ -156,50 +152,45 @@ func (this *UserController) Register(writer http.ResponseWriter, request *http.R user = this.userDao.Create(user) - //做一次登录操作 + //auto login this.innerLogin(writer, request, user) return this.Success(user) } -//编辑一个用户的资料。 func (this *UserController) Edit(writer http.ResponseWriter, request *http.Request) *result.WebResult { avatarUrl := request.FormValue("avatarUrl") uuid := request.FormValue("uuid") + sizeLimitStr := request.FormValue("sizeLimit") - currentUser := this.checkUser(request) - user := this.userDao.CheckByUuid(uuid) + user := this.checkUser(request) + currentUser := this.userDao.CheckByUuid(uuid) - if currentUser.Role == USER_ROLE_ADMINISTRATOR { - //只有管理员可以改变用户上传的大小 - //判断用户上传大小限制。 - sizeLimitStr := request.FormValue("sizeLimit") + if user.Role == USER_ROLE_ADMINISTRATOR { + //only admin can edit user's sizeLimit var sizeLimit int64 = 0 if sizeLimitStr == "" { - panic("用户上传限制必填!") + panic("user's limit size is required") } else { - intsizeLimit, err := strconv.Atoi(sizeLimitStr) + intSizeLimit, err := strconv.Atoi(sizeLimitStr) if err != nil { this.PanicError(err) } - sizeLimit = int64(intsizeLimit) - } - user.SizeLimit = sizeLimit - } else { - if currentUser.Uuid != uuid { - panic(result.UNAUTHORIZED) + sizeLimit = int64(intSizeLimit) } + currentUser.SizeLimit = sizeLimit + } else if user.Uuid != uuid { + panic(result.UNAUTHORIZED) } - user.AvatarUrl = avatarUrl + currentUser.AvatarUrl = avatarUrl - user = this.userDao.Save(user) + currentUser = this.userDao.Save(currentUser) - return this.Success(user) + return this.Success(currentUser) } -//获取用户详情 func (this *UserController) Detail(writer http.ResponseWriter, request *http.Request) *result.WebResult { uuid := request.FormValue("uuid") @@ -210,13 +201,12 @@ func (this *UserController) Detail(writer http.ResponseWriter, request *http.Req } -//退出登录 func (this *UserController) Logout(writer http.ResponseWriter, request *http.Request) *result.WebResult { - //session置为过期 + //expire session. sessionCookie, err := request.Cookie(core.COOKIE_AUTH_KEY) if err != nil { - return this.Success("已经退出登录了!") + return this.Success("OK") } sessionId := sessionCookie.Value @@ -227,13 +217,13 @@ func (this *UserController) Logout(writer http.ResponseWriter, request *http.Req this.sessionDao.Save(session) } - //删掉session缓存 + //delete session. _, err = core.CONTEXT.GetSessionCache().Delete(sessionId) if err != nil { - this.logger.Error("删除用户session缓存时出错") + this.logger.Error("error while deleting session.") } - //清空客户端的cookie. + //clear cookie. expiration := time.Now() expiration = expiration.AddDate(-1, 0, 0) cookie := http.Cookie{ @@ -243,10 +233,9 @@ func (this *UserController) Logout(writer http.ResponseWriter, request *http.Req Expires: expiration} http.SetCookie(writer, &cookie) - return this.Success("退出成功!") + return this.Success("OK") } -//获取用户列表 管理员的权限。 func (this *UserController) Page(writer http.ResponseWriter, request *http.Request) *result.WebResult { pageStr := request.FormValue("page") @@ -298,14 +287,13 @@ func (this *UserController) Page(writer http.ResponseWriter, request *http.Reque return this.Success(pager) } -//修改用户状态 func (this *UserController) ToggleStatus(writer http.ResponseWriter, request *http.Request) *result.WebResult { uuid := request.FormValue("uuid") currentUser := this.userDao.CheckByUuid(uuid) user := this.checkUser(request) if uuid == user.Uuid { - panic(result.Unauthorized("你不能操作自己的状态。")) + panic(result.UNAUTHORIZED) } if currentUser.Status == USER_STATUS_OK { @@ -320,17 +308,15 @@ func (this *UserController) ToggleStatus(writer http.ResponseWriter, request *ht } -//变身为指定用户。 func (this *UserController) Transfiguration(writer http.ResponseWriter, request *http.Request) *result.WebResult { uuid := request.FormValue("uuid") currentUser := this.userDao.CheckByUuid(uuid) - //有效期10分钟 + //expire after 10 minutes. expiration := time.Now() expiration = expiration.Add(10 * time.Minute) - //持久化用户的session. session := &Session{ UserUuid: currentUser.Uuid, Ip: util.GetIpAddress(request), @@ -343,7 +329,6 @@ func (this *UserController) Transfiguration(writer http.ResponseWriter, request return this.Success(session.Uuid) } -//用户修改密码 func (this *UserController) ChangePassword(writer http.ResponseWriter, request *http.Request) *result.WebResult { oldPassword := request.FormValue("oldPassword") @@ -354,8 +339,8 @@ func (this *UserController) ChangePassword(writer http.ResponseWriter, request * user := this.checkUser(request) - //如果是demo账号,不提供修改密码的功能。 - if user.Username == "demo" { + //if username is demo, cannot change password. + if user.Username == USERNAME_DEMO { return this.Success(user) } @@ -370,7 +355,7 @@ func (this *UserController) ChangePassword(writer http.ResponseWriter, request * return this.Success(user) } -//管理员重置用户密码 +//admin reset password. func (this *UserController) ResetPassword(writer http.ResponseWriter, request *http.Request) *result.WebResult { userUuid := request.FormValue("userUuid") diff --git a/code/rest/user_dao.go b/code/rest/user_dao.go index e03cc06..536f63c 100644 --- a/code/rest/user_dao.go +++ b/code/rest/user_dao.go @@ -16,11 +16,10 @@ func (this *UserDao) Init() { this.BaseDao.Init() } -//创建用户 func (this *UserDao) Create(user *User) *User { if user == nil { - panic("参数不能为nil") + panic(result.BadRequest("user cannot be nil")) } timeUUID, _ := uuid.NewV4() @@ -59,7 +58,6 @@ func (this *UserDao) CheckByUuid(uuid string) *User { return entity } -//查询用户。 func (this *UserDao) FindByUsername(username string) *User { var user = &User{} @@ -74,7 +72,6 @@ func (this *UserDao) FindByUsername(username string) *User { return user } -//显示用户列表。 func (this *UserDao) Page(page int, pageSize int, username string, email string, phone string, status string, sortArray []builder.OrderPair) *Pager { var wp = &builder.WherePair{} @@ -114,7 +111,6 @@ func (this *UserDao) Page(page int, pageSize int, username string, email string, return pager } -//查询某个用户名是否已经有用户了 func (this *UserDao) CountByUsername(username string) int { var count int db := core.CONTEXT.GetDB(). @@ -125,7 +121,6 @@ func (this *UserDao) CountByUsername(username string) int { return count } -//保存用户 func (this *UserDao) Save(user *User) *User { user.UpdateTime = time.Now() diff --git a/code/rest/user_model.go b/code/rest/user_model.go index 4499448..1f69b9f 100644 --- a/code/rest/user_model.go +++ b/code/rest/user_model.go @@ -6,21 +6,27 @@ import ( ) const ( - //游客身份 + //guest USER_ROLE_GUEST = "GUEST" - //普通注册用户 + //normal user USER_ROLE_USER = "USER" - //管理员 + //administrator USER_ROLE_ADMINISTRATOR = "ADMINISTRATOR" ) const ( - //正常状态 + //ok USER_STATUS_OK = "OK" - //被禁用 + //disabled USER_STATUS_DISABLED = "DISABLED" ) +const ( + //username pattern + USERNAME_PATTERN = `^[0-9a-z_]+$` + USERNAME_DEMO = "demo" +) + type User struct { Base Role string `json:"role" gorm:"type:varchar(45)"` diff --git a/code/rest/user_service.go b/code/rest/user_service.go index 35d9d77..bba3b09 100644 --- a/code/rest/user_service.go +++ b/code/rest/user_service.go @@ -5,7 +5,7 @@ import ( "github.com/eyebluecn/tank/code/tool/cache" "github.com/eyebluecn/tank/code/tool/result" "github.com/eyebluecn/tank/code/tool/util" - uuid "github.com/nu7hatch/gouuid" + gouuid "github.com/nu7hatch/gouuid" "net/http" "time" ) @@ -16,7 +16,7 @@ type UserService struct { userDao *UserDao sessionDao *SessionDao - //操作文件的锁。 + //file lock locker *cache.Table } @@ -33,31 +33,27 @@ func (this *UserService) Init() { this.sessionDao = b } - //创建一个用于存储用户文件锁的缓存。 + //create a lock cache. this.locker = cache.NewTable() } -//对某个用户进行加锁。加锁阶段用户是不允许操作文件的。 +//lock a user's operation. If lock, user cannot operate file. func (this *UserService) MatterLock(userUuid string) { - //如果已经是锁住的状态,直接报错 - //去缓存中捞取 cacheItem, err := this.locker.Value(userUuid) if err != nil { this.logger.Error("error while get cache" + err.Error()) } - //当前被锁住了。 if cacheItem != nil && cacheItem.Data() != nil { panic(result.BadRequest("file is being operating, retry later")) } - //添加一把新锁,有效期为12小时 duration := 12 * time.Hour this.locker.Add(userUuid, duration, true) } -//对某个用户解锁,解锁后用户可以操作文件。 +//unlock func (this *UserService) MatterUnlock(userUuid string) { exist := this.locker.Exists(userUuid) @@ -65,58 +61,53 @@ func (this *UserService) MatterUnlock(userUuid string) { _, err := this.locker.Delete(userUuid) this.PanicError(err) } else { - this.logger.Error("%s已经不存在matter锁了,解锁错误。", userUuid) + this.logger.Error("unlock error. %s has no matter lock ", userUuid) } } -//装载session信息,如果session没有了根据cookie去装填用户信息。 -//在所有的路由最初会调用这个方法 -//1. 支持cookie形式 2.支持入参传入username和password 3.支持Basic Auth +//load session to SessionCache. This method will be invoked in every request. +//authorize by 1. cookie 2. username and password in request form. 3. Basic Auth func (this *UserService) PreHandle(writer http.ResponseWriter, request *http.Request) { - //登录身份有效期以数据库中记录的为准 - - //验证用户是否已经登录。 sessionId := util.GetSessionUuidFromRequest(request, core.COOKIE_AUTH_KEY) if sessionId != "" { - //去缓存中捞取 cacheItem, err := core.CONTEXT.GetSessionCache().Value(sessionId) if err != nil { - this.logger.Error("获取缓存时出错了" + err.Error()) + this.logger.Error("occur error will get session cache %s", err.Error()) } - //缓存中没有,尝试去数据库捞取 + //if no cache. try to find in db. if cacheItem == nil || cacheItem.Data() == nil { session := this.sessionDao.FindByUuid(sessionId) if session != nil { duration := session.ExpireTime.Sub(time.Now()) if duration <= 0 { - this.logger.Error("登录信息已过期") + this.logger.Error("login info has expired.") } else { user := this.userDao.FindByUuid(session.UserUuid) if user != nil { - //将用户装填进缓存中 core.CONTEXT.GetSessionCache().Add(sessionId, duration, user) } else { - this.logger.Error("没有找到对应的user %s", session.UserUuid) + this.logger.Error("no user with sessionId %s", session.UserUuid) } } } } } - //再尝试读取一次,这次从 USERNAME_KEY PASSWORD_KEY 中装填用户登录信息 + //try to auth by USERNAME_KEY PASSWORD_KEY cacheItem, err := core.CONTEXT.GetSessionCache().Value(sessionId) if err != nil { - this.logger.Error("获取缓存时出错了" + err.Error()) + this.logger.Error("occur error will get session cache %s", err.Error()) } if cacheItem == nil || cacheItem.Data() == nil { username := request.FormValue(core.USERNAME_KEY) password := request.FormValue(core.PASSWORD_KEY) + //try to read from BasicAuth if username == "" || password == "" { username, password, _ = request.BasicAuth() } @@ -125,19 +116,18 @@ func (this *UserService) PreHandle(writer http.ResponseWriter, request *http.Req user := this.userDao.FindByUsername(username) if user == nil { - this.logger.Error("%s 用户名或密码错误", core.USERNAME_KEY) + this.logger.Error("%s no such user in db.", username) } else { if !util.MatchBcrypt(password, user.Password) { - this.logger.Error("%s 用户名或密码错误", core.USERNAME_KEY) + this.logger.Error("%s password error", username) } else { - //装填一个临时的session用作后续使用。 - this.logger.Info("准备装载一个临时的用作。") - timeUUID, _ := uuid.NewV4() + + this.logger.Info("load a temp session by username and password.") + timeUUID, _ := gouuid.NewV4() uuidStr := string(timeUUID.String()) request.Form[core.COOKIE_AUTH_KEY] = []string{uuidStr} - //将用户装填进缓存中 core.CONTEXT.GetSessionCache().Add(uuidStr, 10*time.Second, user) } } diff --git a/code/support/tank_application.go b/code/support/tank_application.go index 2bf88b0..9467372 100644 --- a/code/support/tank_application.go +++ b/code/support/tank_application.go @@ -17,39 +17,37 @@ import ( ) const ( - //启动web服务,默认是这种方式 + //start web. This is the default mode. MODE_WEB = "web" - //映射本地文件到云盘中 + //mirror local files to EyeblueTank. MODE_MIRROR = "mirror" - //将远程的一个文件爬取到蓝眼云盘中 + //crawl remote file to EyeblueTank MODE_CRAWL = "crawl" - //TODO: 查看当前蓝眼云盘版本 + //Current version. MODE_VERSION = "version" ) -//命令行输入相关的对象 type TankApplication struct { - //模式 + //mode mode string - //蓝眼云盘的主机,需要带上协议和端口号。默认: http://127.0.0.1:core.DEFAULT_SERVER_PORT + //EyeblueTank host and port default: http://127.0.0.1:core.DEFAULT_SERVER_PORT host string - //用户名 + //username username string - //密码 + //password password string - //源文件/文件夹,本地绝对路径/远程资源url + //source file/directory different mode has different usage. src string - //目标(表示的是文件夹)路径,蓝眼云盘中的路径。相对于root的路径。 + //destination directory path(relative to root) in EyeblueTank dest string - //同名文件或文件夹是否直接替换 true 全部替换; false 跳过 + //true: overwrite, false:skip overwrite bool - //拉取文件存储的名称 - filename string + filename string } -//启动应用。可能是web形式,也可能是命令行工具。 +//Start the application. func (this *TankApplication) Start() { defer func() { @@ -58,7 +56,6 @@ func (this *TankApplication) Start() { } }() - //超级管理员信息 modePtr := flag.String("mode", this.mode, "cli mode web/mirror/crawl") hostPtr := flag.String("host", this.username, "tank host") usernamePtr := flag.String("username", this.username, "username") @@ -68,7 +65,7 @@ func (this *TankApplication) Start() { overwritePtr := flag.Bool("overwrite", this.overwrite, "whether same file overwrite") filenamePtr := flag.String("filename", this.filename, "filename when crawl") - //flag.Parse()方法必须要在使用之前调用。 + //flag.Parse() must invoke before use. flag.Parse() this.mode = *modePtr @@ -80,36 +77,30 @@ func (this *TankApplication) Start() { this.overwrite = *overwritePtr this.filename = *filenamePtr - //默认采用web的形式启动 + //default start as web. if this.mode == "" || strings.ToLower(this.mode) == MODE_WEB { - //直接web启动 this.HandleWeb() } else if strings.ToLower(this.mode) == MODE_VERSION { - //直接web启动 this.HandleVersion() } else { - //准备蓝眼云盘地址 + //default host. if this.host == "" { this.host = fmt.Sprintf("http://127.0.0.1:%d", core.DEFAULT_SERVER_PORT) } - //准备用户名 if this.username == "" { - panic(result.BadRequest("%s模式下,用户名必填", this.mode)) + panic(result.BadRequest("in mode %s, username is required", this.mode)) } - //准备密码 if this.password == "" { if util.EnvDevelopment() { - - panic(result.BadRequest("IDE中请运行请直接使用 -password yourPassword 的形式输入密码")) - + panic(result.BadRequest("If run in IDE, use -password yourPassword to input password")) } else { fmt.Print("Enter Password:") @@ -125,42 +116,39 @@ func (this *TankApplication) Start() { if strings.ToLower(this.mode) == MODE_MIRROR { - //映射本地文件到蓝眼云盘 this.HandleMirror() } else if strings.ToLower(this.mode) == MODE_CRAWL { - //将远程文件拉取到蓝眼云盘中 this.HandleCrawl() } else { - panic(result.BadRequest("不能处理命名行模式: %s \r\n", this.mode)) + panic(result.BadRequest("cannot handle mode %s \r\n", this.mode)) } } } -//采用Web的方式启动应用 func (this *TankApplication) HandleWeb() { - //第1步。日志 + //Step 1. Logger tankLogger := &TankLogger{} core.LOGGER = tankLogger tankLogger.Init() defer tankLogger.Destroy() - //第2步。配置 + //Step 2. Configuration tankConfig := &TankConfig{} core.CONFIG = tankConfig tankConfig.Init() - //第3步。全局运行的上下文 + //Step 3. Global Context tankContext := &TankContext{} core.CONTEXT = tankContext tankContext.Init() defer tankContext.Destroy() - //第4步。启动http服务 + //Step 4. Start http http.Handle("/", core.CONTEXT) core.LOGGER.Info("App started at http://localhost:%v", core.CONFIG.ServerPort()) @@ -172,17 +160,16 @@ func (this *TankApplication) HandleWeb() { } -//处理本地映射的情形 func (this *TankApplication) HandleMirror() { if this.src == "" { - panic("src 必填") + panic("src is required") } if this.dest == "" { - panic("dest 必填") + panic("dest is required") } - fmt.Printf("开始映射本地文件 %s 到蓝眼云盘 %s\r\n", this.src, this.dest) + fmt.Printf("start mirror %s to EyeblueTank %s\r\n", this.src, this.dest) urlString := fmt.Sprintf("%s/api/matter/mirror", this.host) @@ -203,7 +190,7 @@ func (this *TankApplication) HandleMirror() { err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(bodyBytes, webResult) if err != nil { - fmt.Printf("返回格式错误!%s \r\n", err.Error()) + fmt.Printf("error response format %s \r\n", err.Error()) return } @@ -215,21 +202,20 @@ func (this *TankApplication) HandleMirror() { } -//拉取远程文件到本地。 func (this *TankApplication) HandleCrawl() { if this.src == "" { - panic("src 必填") + panic("src is required") } if this.dest == "" { - panic("dest 必填") + panic("dest is required") } if this.filename == "" { - panic("filename 必填") + panic("filename is required") } - fmt.Printf("开始映射拉取远程文件 %s 到蓝眼云盘 %s\r\n", this.src, this.dest) + fmt.Printf("crawl %s to EyeblueTank %s\r\n", this.src, this.dest) urlString := fmt.Sprintf("%s/api/matter/crawl", this.host) @@ -250,7 +236,7 @@ func (this *TankApplication) HandleCrawl() { err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(bodyBytes, webResult) if err != nil { - fmt.Printf("返回格式错误!%s \r\n", err.Error()) + fmt.Printf("Error response format %s \r\n", err.Error()) return } diff --git a/code/support/tank_config.go b/code/support/tank_config.go index 32f6779..e35f164 100644 --- a/code/support/tank_config.go +++ b/code/support/tank_config.go @@ -10,76 +10,68 @@ import ( "unsafe" ) -/* -如果你需要在本地127.0.0.1创建默认的数据库和账号,使用以下语句。 -create database tank; -grant all privileges on tank.* to tank identified by 'tank123'; -flush privileges; -*/ - -//依赖外部定义的变量。 type TankConfig struct { - //默认监听端口号 + //server port serverPort int - //网站是否已经完成安装 + //whether installed installed bool - //上传的文件路径,要求不以/结尾。如果没有指定,默认在根目录下的matter文件夹中。eg: /var/www/matter + //file storage location. eg./var/www/matter matterPath string - //数据库连接信息。 + //mysql url. mysqlUrl string - //配置文件中的项 + //configs in tank.json item *ConfigItem } -//和tank.json文件中的键值一一对应。 +//tank.json config items. type ConfigItem struct { - //默认监听端口号 + //server port ServerPort int - //上传的文件路径,要求不以/结尾。如果没有指定,默认在根目录下的matter文件夹中。eg: /var/www/matter + //file storage location. eg./var/www/matter MatterPath string - //mysql相关配置。 - //数据库端口 + //mysql configurations. + //mysql port MysqlPort int - //数据库Host + //mysql host MysqlHost string - //数据库名字 + //mysql schema MysqlSchema string - //用户名 + //mysql username MysqlUsername string - //密码 + //mysql password MysqlPassword string } -//验证配置文件的正确性。 +//validate whether the config file is ok func (this *ConfigItem) validate() bool { if this.ServerPort == 0 { - core.LOGGER.Error("ServerPort 未配置") + core.LOGGER.Error("ServerPort is not configured") return false } if this.MysqlUsername == "" { - core.LOGGER.Error("MysqlUsername 未配置") + core.LOGGER.Error("MysqlUsername is not configured") return false } if this.MysqlPassword == "" { - core.LOGGER.Error("MysqlPassword 未配置") + core.LOGGER.Error("MysqlPassword is not configured") return false } if this.MysqlHost == "" { - core.LOGGER.Error("MysqlHost 未配置") + core.LOGGER.Error("MysqlHost is not configured") return false } if this.MysqlPort == 0 { - core.LOGGER.Error("MysqlPort 未配置") + core.LOGGER.Error("MysqlPort is not configured") return false } if this.MysqlSchema == "" { - core.LOGGER.Error("MysqlSchema 未配置") + core.LOGGER.Error("MysqlSchema is not configured") return false } @@ -87,12 +79,11 @@ func (this *ConfigItem) validate() bool { } -//验证配置文件是否完好 func (this *TankConfig) Init() { - //JSON初始化 + //JSON init. jsoniter.RegisterTypeDecoderFunc("time.Time", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { - //如果使用time.UTC,那么时间会相差8小时 + //if use time.UTC there will be 8 hours gap. t, err := time.ParseInLocation("2006-01-02 15:04:05", iter.ReadString(), time.Local) if err != nil { iter.Error = err @@ -103,45 +94,44 @@ func (this *TankConfig) Init() { jsoniter.RegisterTypeEncoderFunc("time.Time", func(ptr unsafe.Pointer, stream *jsoniter.Stream) { t := *((*time.Time)(ptr)) - //如果使用time.UTC,那么时间会相差8小时 + //if use time.UTC there will be 8 hours gap. stream.WriteString(t.Local().Format("2006-01-02 15:04:05")) }, nil) - //默认从core.DEFAULT_SERVER_PORT端口启动 + //default server port. this.serverPort = core.DEFAULT_SERVER_PORT this.ReadFromConfigFile() } -//系统如果安装好了就调用这个方法。 func (this *TankConfig) ReadFromConfigFile() { - //读取配置文件 + //read from tank.json filePath := util.GetConfPath() + "/tank.json" content, err := ioutil.ReadFile(filePath) if err != nil { - core.LOGGER.Warn("无法找到配置文件:%s 即将进入安装过程!", filePath) + core.LOGGER.Warn("cannot find config file %s, installation will start!", filePath) this.installed = false } else { this.item = &ConfigItem{} - core.LOGGER.Warn("读取配置文件:%s", filePath) + core.LOGGER.Warn("read config file %s", filePath) err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(content, this.item) if err != nil { - core.LOGGER.Error("配置文件格式错误! 即将进入安装过程!") + core.LOGGER.Error("config file error, installation will start!") this.installed = false return } - //验证项是否齐全 + //check the integrity itemValidate := this.item.validate() if !itemValidate { - core.LOGGER.Error("配置文件信息不齐全! 即将进入安装过程!") + core.LOGGER.Error("config file not integrity, installation will start!") this.installed = false return } - //使用配置项中的文件路径 + //use default file location. if this.item.MatterPath == "" { this.matterPath = util.GetHomePath() + "/matter" } else { @@ -149,7 +139,7 @@ func (this *TankConfig) ReadFromConfigFile() { } util.MakeDirAll(this.matterPath) - //使用配置项中的端口 + //use default server port if this.item.ServerPort != 0 { this.serverPort = this.item.ServerPort } @@ -157,57 +147,50 @@ func (this *TankConfig) ReadFromConfigFile() { this.mysqlUrl = util.GetMysqlUrl(this.item.MysqlPort, this.item.MysqlHost, this.item.MysqlSchema, this.item.MysqlUsername, this.item.MysqlPassword) this.installed = true - core.LOGGER.Info("使用配置文件:%s", filePath) - core.LOGGER.Info("上传文件存放路径:%s", this.matterPath) + core.LOGGER.Info("use config file: %s", filePath) + core.LOGGER.Info("file storage location: %s", this.matterPath) } } -//是否已经安装 +//whether installed. func (this *TankConfig) Installed() bool { return this.installed } -//启动端口 +//server port func (this *TankConfig) ServerPort() int { return this.serverPort } -//获取mysql链接 +//mysql url func (this *TankConfig) MysqlUrl() string { return this.mysqlUrl } -//文件存放路径 +//matter path func (this *TankConfig) MatterPath() string { - return this.matterPath } -//完成安装过程,主要是要将配置写入到文件中 +//Finish the installation. Write config to tank.json func (this *TankConfig) FinishInstall(mysqlPort int, mysqlHost string, mysqlSchema string, mysqlUsername string, mysqlPassword string) { var configItem = &ConfigItem{ - //默认监听端口号 + //server port ServerPort: core.CONFIG.ServerPort(), - //上传的文件路径,要求不以/结尾。如果没有指定,默认在根目录下的matter文件夹中。eg: /var/www/matter - MatterPath: core.CONFIG.MatterPath(), - //mysql相关配置。 - //数据库端口 - MysqlPort: mysqlPort, - //数据库Host - MysqlHost: mysqlHost, - //数据库名字 - MysqlSchema: mysqlSchema, - //用户名 + //file storage location. eg./var/www/matter + MatterPath: core.CONFIG.MatterPath(), + MysqlPort: mysqlPort, + MysqlHost: mysqlHost, + MysqlSchema: mysqlSchema, MysqlUsername: mysqlUsername, - //密码 MysqlPassword: mysqlPassword, } - //用json的方式输出返回值。为了让格式更好看。 + //pretty json. jsonStr, _ := jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent(configItem, "", " ") - //写入到配置文件中(不能使用os.O_APPEND 否则会追加) + //Write to tank.json (cannot use os.O_APPEND or append) filePath := util.GetConfPath() + "/tank.json" f, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0777) core.PanicError(err) diff --git a/code/support/tank_context.go b/code/support/tank_context.go index 00dfcac..32dc75a 100644 --- a/code/support/tank_context.go +++ b/code/support/tank_context.go @@ -1,8 +1,6 @@ package support import ( - "fmt" - "github.com/eyebluecn/tank/code/core" "github.com/eyebluecn/tank/code/rest" "github.com/eyebluecn/tank/code/tool/cache" @@ -11,45 +9,42 @@ import ( "reflect" ) -//上下文,管理数据库连接,管理所有路由请求,管理所有的单例component. type TankContext struct { - //数据库连接 + //db connection db *gorm.DB - //session缓存 + //session cache SessionCache *cache.Table - //各类的Bean Map。这里面是包含ControllerMap中所有元素 + //bean map. BeanMap map[string]core.Bean - //只包含了Controller的map + //controller map ControllerMap map[string]core.Controller - //处理所有路由请求 + //router Router *TankRouter } -//初始化上下文 func (this *TankContext) Init() { - //创建一个用于存储session的缓存。 + //create session cache this.SessionCache = cache.NewTable() - //初始化Map + //init map this.BeanMap = make(map[string]core.Bean) this.ControllerMap = make(map[string]core.Controller) - //注册各类Beans.在这个方法里面顺便把Controller装入ControllerMap中去。 + //register beans. This method will put Controllers to ControllerMap. this.registerBeans() - //初始化每个bean. + //init every bean. this.initBeans() - //初始化Router. 这个方法要在Bean注册好了之后才能。 + //create and init router. this.Router = NewRouter() - //如果数据库信息配置好了,就直接打开数据库连接 同时执行Bean的ConfigPost方法 + //if the application is installed. Bean's Bootstrap method will be invoked. this.InstallOk() } -//获取数据库对象 func (this *TankContext) GetDB() *gorm.DB { return this.db } @@ -68,7 +63,7 @@ func (this *TankContext) Cleanup() { } } -//响应http的能力 +//can serve as http server. func (this *TankContext) ServeHTTP(writer http.ResponseWriter, request *http.Request) { this.Router.ServeHTTP(writer, request) } @@ -82,7 +77,7 @@ func (this *TankContext) OpenDb() { core.LOGGER.Panic("failed to connect mysql database") } - //是否打开sql日志(在调试阶段可以打开,以方便查看执行的SQL) + //whether open the db log. (only true when debug) this.db.LogMode(false) } @@ -91,12 +86,11 @@ func (this *TankContext) CloseDb() { if this.db != nil { err := this.db.Close() if err != nil { - core.LOGGER.Error("关闭数据库连接出错 %s", err.Error()) + core.LOGGER.Error("occur error when closing db %s", err.Error()) } } } -//注册一个Bean func (this *TankContext) registerBean(bean core.Bean) { typeOf := reflect.TypeOf(bean) @@ -104,13 +98,12 @@ func (this *TankContext) registerBean(bean core.Bean) { if element, ok := bean.(core.Bean); ok { - err := fmt.Sprintf("【%s】已经被注册了,跳过。", typeName) if _, ok := this.BeanMap[typeName]; ok { - core.LOGGER.Error(fmt.Sprintf(err)) + core.LOGGER.Error("%s has been registerd, skip", typeName) } else { this.BeanMap[typeName] = element - //看看是不是controller类型,如果是,那么单独放在ControllerMap中。 + //if is controller type, put into ControllerMap if controller, ok1 := bean.(core.Controller); ok1 { this.ControllerMap[typeName] = controller } @@ -118,12 +111,11 @@ func (this *TankContext) registerBean(bean core.Bean) { } } else { - core.LOGGER.Panic("注册的【%s】不是Bean类型。", typeName) + core.LOGGER.Panic("%s is not the Bean type", typeName) } } -//注册各个Beans func (this *TankContext) registerBeans() { //alien @@ -188,7 +180,6 @@ func (this *TankContext) registerBeans() { } -//从Map中获取某个Bean. func (this *TankContext) GetBean(bean core.Bean) core.Bean { typeOf := reflect.TypeOf(bean) @@ -197,12 +188,11 @@ func (this *TankContext) GetBean(bean core.Bean) core.Bean { if val, ok := this.BeanMap[typeName]; ok { return val } else { - core.LOGGER.Panic("【%s】没有注册。", typeName) + core.LOGGER.Panic("%s not registered", typeName) return nil } } -//初始化每个Bean func (this *TankContext) initBeans() { for _, bean := range this.BeanMap { @@ -210,7 +200,7 @@ func (this *TankContext) initBeans() { } } -//系统如果安装好了就调用这个方法。 +//if application installed. invoke this method. func (this *TankContext) InstallOk() { if core.CONFIG.Installed() { @@ -223,7 +213,6 @@ func (this *TankContext) InstallOk() { } -//销毁的方法 func (this *TankContext) Destroy() { this.CloseDb() } diff --git a/code/support/tank_logger.go b/code/support/tank_logger.go index 2d1213f..a14813a 100644 --- a/code/support/tank_logger.go +++ b/code/support/tank_logger.go @@ -12,14 +12,12 @@ import ( "time" ) -//在Logger的基础上包装一个全新的Logger. type TankLogger struct { - //加锁,在维护日志期间,禁止写入日志。 + //lock. when maintaining, cannot write sync.RWMutex - - //继承logger + //extend logger goLogger *log.Logger - //日志记录所在的文件 + //log file. file *os.File } @@ -27,13 +25,12 @@ func (this *TankLogger) Init() { this.openFile() - //每日00:00整理日志。 + this.Info("[cron job] Every day 00:00 maintain log file.") expression := "0 0 0 * * ?" cronJob := cron.New() err := cronJob.AddFunc(expression, this.maintain) core.PanicError(err) cronJob.Start() - this.Info("[cron job] 每日00:00维护日志") } @@ -41,12 +38,12 @@ func (this *TankLogger) Destroy() { this.closeFile() } -//处理日志的统一方法。 +//uniform log method func (this *TankLogger) Log(prefix string, format string, v ...interface{}) { content := fmt.Sprintf(format+"\r\n", v...) - //控制台中打印日志,记录行号。 + //print to console with line number _, file, line, ok := runtime.Caller(2) if !ok { file = "???" @@ -58,14 +55,12 @@ func (this *TankLogger) Log(prefix string, format string, v ...interface{}) { this.goLogger.SetPrefix(prefix) - //每一行我们加上换行符 err := this.goLogger.Output(3, content) if err != nil { fmt.Printf("occur error while logging %s \r\n", err.Error()) } } -//处理日志的统一方法。 func (this *TankLogger) Debug(format string, v ...interface{}) { this.Log("[DEBUG]", format, v...) } @@ -88,59 +83,59 @@ func (this *TankLogger) Panic(format string, v ...interface{}) { } //将日志写入到今天的日期中(该方法内必须使用异步方法记录日志,否则会引发死锁) +//rename log file. func (this *TankLogger) maintain() { - this.Info("每日维护日志") + this.Info("maintain log") this.Lock() defer this.Unlock() - //首先关闭文件。 this.closeFile() - //日志归类到昨天 + //archive to yesterday destPath := util.GetLogPath() + "/tank-" + util.ConvertTimeToDateString(util.Yesterday()) + ".log" - //直接重命名文件 + //rename the log file. err := os.Rename(this.fileName(), destPath) if err != nil { - this.Error("重命名文件出错", err.Error()) + this.Error("occur error while renaming log file %s %s", destPath, err.Error()) } - //再次打开文件 + //reopen this.openFile() - //删除一个月之前的日志文件。 + //delete log files a month ago monthAgo := time.Now() monthAgo = monthAgo.AddDate(0, -1, 0) oldDestPath := util.GetLogPath() + "/tank-" + util.ConvertTimeToDateString(monthAgo) + ".log" - this.Log("删除日志文件 %s", oldDestPath) + this.Log("try to delete log file %s", oldDestPath) - //删除文件 + //delete log file exists := util.PathExists(oldDestPath) if exists { err = os.Remove(oldDestPath) if err != nil { - this.Error("删除磁盘上的文件%s 出错 %s", oldDestPath, err.Error()) + this.Error("occur error while deleting log file %s %s", oldDestPath, err.Error()) } } else { - this.Error("日志文件 %s 不存在,无需删除", oldDestPath) + this.Error("log file %s not exist, skip", oldDestPath) } } -//日志名称 +//log file name func (this *TankLogger) fileName() string { return util.GetLogPath() + "/tank.log" } -//打开日志文件 +//open log file func (this *TankLogger) openFile() { - //日志输出到文件中 文件打开后暂时不关闭 - fmt.Printf("使用日志文件 %s\r\n", this.fileName()) + // not close log file immediately + fmt.Printf("use log file %s\r\n", this.fileName()) f, err := os.OpenFile(this.fileName(), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { - panic("日志文件无法正常打开: " + err.Error()) + panic("cannot open log file " + err.Error()) } this.goLogger = log.New(f, "", log.Ltime|log.Lshortfile) @@ -152,12 +147,12 @@ func (this *TankLogger) openFile() { this.file = f } -//关闭日志文件 +//close log file. func (this *TankLogger) closeFile() { if this.file != nil { err := this.file.Close() if err != nil { - panic("尝试关闭日志时出错: " + err.Error()) + panic("occur error while closing log file: " + err.Error()) } } } diff --git a/code/support/tank_router.go b/code/support/tank_router.go index 2ad5858..f9a870f 100644 --- a/code/support/tank_router.go +++ b/code/support/tank_router.go @@ -15,7 +15,6 @@ import ( "time" ) -//用于处理所有前来的请求 type TankRouter struct { installController *rest.InstallController footprintService *rest.FootprintService @@ -24,7 +23,6 @@ type TankRouter struct { installRouteMap map[string]func(writer http.ResponseWriter, request *http.Request) } -//构造方法 func NewRouter() *TankRouter { router := &TankRouter{ routeMap: make(map[string]func(writer http.ResponseWriter, request *http.Request)), @@ -37,19 +35,19 @@ func NewRouter() *TankRouter { router.installController = b } - //装载userService. + //load userService b = core.CONTEXT.GetBean(router.userService) if b, ok := b.(*rest.UserService); ok { router.userService = b } - //装载footprintService + //load footprintService b = core.CONTEXT.GetBean(router.footprintService) if b, ok := b.(*rest.FootprintService); ok { router.footprintService = b } - //将Controller中的路由规则装载进来,InstallController中的除外 + //load Controllers except InstallController for _, controller := range core.CONTEXT.GetControllerMap() { if controller == router.installController { @@ -69,18 +67,18 @@ func NewRouter() *TankRouter { } -//全局的异常捕获 +//catch global panic. func (this *TankRouter) GlobalPanicHandler(writer http.ResponseWriter, request *http.Request, startTime time.Time) { if err := recover(); err != nil { - //控制台中打印日志,记录行号。 + //get panic file and line number. _, file, line, ok := runtime.Caller(2) if !ok { file = "???" line = 0 } - //全局未知异常 + //unkown panic if strings.HasSuffix(file, "runtime/panic.go") { _, file, line, ok = runtime.Caller(4) if !ok { @@ -88,7 +86,7 @@ func (this *TankRouter) GlobalPanicHandler(writer http.ResponseWriter, request * line = 0 } } - //全局方便的异常拦截 + //async panic if strings.HasSuffix(file, "core/handler.go") { _, file, line, ok = runtime.Caller(4) if !ok { @@ -101,71 +99,69 @@ func (this *TankRouter) GlobalPanicHandler(writer http.ResponseWriter, request * var webResult *result.WebResult = nil if value, ok := err.(string); ok { - //一个字符串,默认是请求错误。 + //string, default as BadRequest. webResult = result.CustomWebResult(result.BAD_REQUEST, value) } else if value, ok := err.(*result.WebResult); ok { - //一个WebResult对象 + //*result.WebResult webResult = value } else if value, ok := err.(*result.CodeWrapper); ok { - //一个WebResult对象 + //*result.CodeWrapper webResult = result.ConstWebResult(value) } else if value, ok := err.(error); ok { - //一个普通的错误对象 + //normal error webResult = result.CustomWebResult(result.UNKNOWN, value.Error()) } else { - //其他不能识别的内容 + //other error webResult = result.ConstWebResult(result.UNKNOWN) } - //修改http code码 + //change the http status. writer.WriteHeader(result.FetchHttpStatus(webResult.Code)) - //输出的是json格式 返回的内容申明是json,utf-8 + //if json, set the Content-Type to json. writer.Header().Set("Content-Type", "application/json;charset=UTF-8") - //用json的方式输出返回值。 + //write the response. b, _ := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(webResult) - //写到输出流中 + //write to writer. _, err := fmt.Fprintf(writer, string(b)) if err != nil { - fmt.Printf("输出结果时出错了\n") + fmt.Printf("occur error while write response %s\r\n", err.Error()) } - //错误情况记录。 + //log error. go core.RunWithRecovery(func() { this.footprintService.Trace(request, time.Now().Sub(startTime), false) }) } } -//让Router具有处理请求的功能。 func (this *TankRouter) ServeHTTP(writer http.ResponseWriter, request *http.Request) { startTime := time.Now() - //每个请求的入口在这里 - //全局异常处理。 + //global panic handler defer this.GlobalPanicHandler(writer, request, startTime) path := request.URL.Path if strings.HasPrefix(path, "/api") { - //对于IE浏览器,会自动缓存,因此设置不缓存Header. - writer.Header().Set("Pragma", "No-cache") - writer.Header().Set("Cache-Control", "no-cache") - writer.Header().Set("Expires", "0") + //IE browser will cache automatically. disable the cache. + util.DisableCache(writer) if core.CONFIG.Installed() { - //已安装的模式 - //统一处理用户的身份信息。 + //if installed. + + //handler user's auth info. this.userService.PreHandle(writer, request) if handler, ok := this.routeMap[path]; ok { handler(writer, request) } else { - //直接将请求扔给每个controller,看看他们能不能处理,如果都不能处理,那就抛出找不到的错误 + + //dispatch the request to controller's handler. canHandle := false for _, controller := range core.CONTEXT.GetControllerMap() { if handler, exist := controller.HandleRoutes(writer, request); exist { @@ -176,17 +172,17 @@ func (this *TankRouter) ServeHTTP(writer http.ResponseWriter, request *http.Requ } if !canHandle { - panic(result.CustomWebResult(result.NOT_FOUND, fmt.Sprintf("没有找到能够处理%s的方法", path))) + panic(result.CustomWebResult(result.NOT_FOUND, fmt.Sprintf("cannot handle %s", path))) } } - //正常的访问记录会落到这里。 + //log the request go core.RunWithRecovery(func() { this.footprintService.Trace(request, time.Now().Sub(startTime), true) }) } else { - //未安装模式 + //if not installed. try to install. if handler, ok := this.installRouteMap[path]; ok { handler(writer, request) } else { @@ -196,7 +192,7 @@ func (this *TankRouter) ServeHTTP(writer http.ResponseWriter, request *http.Requ } else { - //当作静态资源处理。默认从当前文件下面的static文件夹中取东西。 + //static file. dir := util.GetHtmlPath() requestURI := request.RequestURI diff --git a/code/test/main_test.go b/code/test/main_test.go index d282381..5bc019e 100644 --- a/code/test/main_test.go +++ b/code/test/main_test.go @@ -2,6 +2,7 @@ package test import ( "fmt" + "github.com/eyebluecn/tank/code/core" "github.com/eyebluecn/tank/code/tool/util" "github.com/robfig/cron" "log" @@ -17,7 +18,6 @@ func TestHello(t *testing.T) { } -//测试cron表达式 func TestCron(t *testing.T) { i := 0 @@ -34,12 +34,10 @@ func TestCron(t *testing.T) { c.Start() - //当前线程阻塞 20s time.Sleep(3 * time.Second) } -//测试 时间 func TestDayAgo(t *testing.T) { dayAgo := time.Now() @@ -50,10 +48,3 @@ func TestDayAgo(t *testing.T) { fmt.Printf("%s\n", util.ConvertTimeToDateTimeString(thenDay)) } - -//测试 打包 -func TestZip(t *testing.T) { - - util.Zip("/Users/fusu/d/group/eyeblue/tank/tmp/matter/admin/root/morning", "/Users/fusu/d/group/eyeblue/tank/tmp/log/morning.zip") - -} diff --git a/code/tool/cache/cache.go b/code/tool/cache/cache.go index bea4b6c..9fb8a54 100644 --- a/code/tool/cache/cache.go +++ b/code/tool/cache/cache.go @@ -8,27 +8,25 @@ import ( "time" ) -//缓存项 -//主要借鉴了cache2go https://github.com/muesli/cache2go +//cache2go https://github.com/muesli/cache2go type Item struct { - sync.RWMutex //读写锁 - //缓存键 - key interface{} - //缓存值 + //read write lock + sync.RWMutex + key interface{} data interface{} - // 缓存项的生命期 + // cache duration. duration time.Duration - //创建时间 + // create time createTime time.Time - //最后访问时间 + //last access time accessTime time.Time - //访问次数 + //visit times count int64 - // 在删除缓存项之前调用的回调函数 + // callback after deleting deleteCallback func(key interface{}) } -//新建一项缓存 +//create item. func NewItem(key interface{}, duration time.Duration, data interface{}) *Item { t := time.Now() return &Item{ @@ -42,7 +40,7 @@ func NewItem(key interface{}, duration time.Duration, data interface{}) *Item { } } -//手动获取一下,保持该项 +//keep alive func (item *Item) KeepAlive() { item.Lock() defer item.Unlock() @@ -50,73 +48,63 @@ func (item *Item) KeepAlive() { item.count++ } -//返回生命周期 func (item *Item) Duration() time.Duration { return item.duration } -//返回访问时间。可能并发,加锁 func (item *Item) AccessTime() time.Time { item.RLock() defer item.RUnlock() return item.accessTime } -//返回创建时间 func (item *Item) CreateTime() time.Time { return item.createTime } -//返回访问时间。可能并发,加锁 func (item *Item) Count() int64 { item.RLock() defer item.RUnlock() return item.count } -//返回key值 func (item *Item) Key() interface{} { return item.key } -//返回数据 func (item *Item) Data() interface{} { return item.data } -//设置回调函数 func (item *Item) SetDeleteCallback(f func(interface{})) { item.Lock() defer item.Unlock() item.deleteCallback = f } -// 统一管理缓存项的表 +// table for managing cache items type Table struct { sync.RWMutex - //所有缓存项 + //all cache items items map[interface{}]*Item - // 触发缓存清理的定时器 + // trigger cleanup cleanupTimer *time.Timer - // 缓存清理周期 + // cleanup interval cleanupInterval time.Duration - // 获取一个不存在的缓存项时的回调函数 - loadData func(key interface{}, args ...interface{}) *Item - // 向缓存表增加缓存项时的回调函数 + loadData func(key interface{}, args ...interface{}) *Item + // callback after adding. addedCallback func(item *Item) - // 从缓存表删除一个缓存项时的回调函数 + // callback after deleting deleteCallback func(item *Item) } -// 返回缓存中存储有多少项 func (table *Table) Count() int { table.RLock() defer table.RUnlock() return len(table.items) } -// 遍历所有项 func (table *Table) Foreach(trans func(key interface{}, item *Item)) { table.RLock() defer table.RUnlock() @@ -126,40 +114,34 @@ func (table *Table) Foreach(trans func(key interface{}, item *Item)) { } } -// SetDataLoader配置一个数据加载的回调,当尝试去请求一个不存在的key的时候调用 func (table *Table) SetDataLoader(f func(interface{}, ...interface{}) *Item) { table.Lock() defer table.Unlock() table.loadData = f } -// 添加时的回调函数 func (table *Table) SetAddedCallback(f func(*Item)) { table.Lock() defer table.Unlock() table.addedCallback = f } -// 删除时的回调函数 func (table *Table) SetDeleteCallback(f func(*Item)) { table.Lock() defer table.Unlock() table.deleteCallback = f } -//带有panic恢复的方法 func (table *Table) RunWithRecovery(f func()) { defer func() { if err := recover(); err != nil { - //core.LOGGER.Error("异步任务错误: %v", err) + fmt.Printf("occur error %v \r\n", err) } }() - //执行函数 f() } -//终结检查,被自调整的时间触发 func (table *Table) checkExpire() { table.Lock() if table.cleanupTimer != nil { @@ -171,39 +153,39 @@ func (table *Table) checkExpire() { table.log("Expiration check installed for table") } - // 为了不抢占锁,采用临时的items. + // in order to not take the lock. use temp items. items := table.items table.Unlock() - //为了定时器更准确,我们需要在每一个循环中更新‘now’,不确定是否是有效率的。 + //in order to make timer more precise, update now every loop. now := time.Now() smallestDuration := 0 * time.Second for key, item := range items { - // 取出我们需要的东西,为了不抢占锁 + //take out our things, in order not to take the lock. item.RLock() duration := item.duration accessTime := item.accessTime item.RUnlock() - // 0永久有效 + // 0 means valid. if duration == 0 { continue } if now.Sub(accessTime) >= duration { - //缓存项已经过期 + //cache item expired. _, e := table.Delete(key) if e != nil { - table.log("删除缓存项时出错 %v", e.Error()) + table.log("occur error while deleting %v", e.Error()) } } else { - //查找最靠近结束生命周期的项目 + //find the most possible expire item. if smallestDuration == 0 || duration-now.Sub(accessTime) < smallestDuration { smallestDuration = duration - now.Sub(accessTime) } } } - // 为下次清理设置间隔,自触发机制 + //trigger next clean table.Lock() table.cleanupInterval = smallestDuration if smallestDuration > 0 { @@ -214,26 +196,23 @@ func (table *Table) checkExpire() { table.Unlock() } -// 添加缓存项 +// add item func (table *Table) Add(key interface{}, duration time.Duration, data interface{}) *Item { item := NewItem(key, duration, data) - // 将缓存项放入表中 table.Lock() table.log("Adding item with key %v and lifespan of %v to table", key, duration) table.items[key] = item - // 取出需要的东西,释放锁 expDur := table.cleanupInterval addedItem := table.addedCallback table.Unlock() - // 有回调函数便执行回调 if addedItem != nil { addedItem(item) } - // 如果我们没有设置任何心跳检查定时器或者找一个即将迫近的项目 + //find the most possible expire item. if duration > 0 && (expDur == 0 || duration < expDur) { table.checkExpire() } @@ -241,20 +220,17 @@ func (table *Table) Add(key interface{}, duration time.Duration, data interface{ return item } -// 从缓存中删除项 func (table *Table) Delete(key interface{}) (*Item, error) { table.RLock() r, ok := table.items[key] if !ok { table.RUnlock() - return nil, errors.New(fmt.Sprintf("没有找到%s对应的记录", key)) + return nil, errors.New(fmt.Sprintf("no item with key %s", key)) } - // 取出要用到的东西,释放锁 deleteCallback := table.deleteCallback table.RUnlock() - // 调用删除回调函数 if deleteCallback != nil { deleteCallback(r) } @@ -273,7 +249,7 @@ func (table *Table) Delete(key interface{}) (*Item, error) { return r, nil } -//单纯的检查某个键是否存在 +//check exist. func (table *Table) Exists(key interface{}) bool { table.RLock() defer table.RUnlock() @@ -282,7 +258,7 @@ func (table *Table) Exists(key interface{}) bool { return ok } -//如果存在,返回false. 如果不存在,就去添加一个键,并且返回true +//if exist, return false. if not exist add a key and return true. func (table *Table) NotFoundAdd(key interface{}, lifeSpan time.Duration, data interface{}) bool { table.Lock() @@ -295,24 +271,20 @@ func (table *Table) NotFoundAdd(key interface{}, lifeSpan time.Duration, data in table.log("Adding item with key %v and lifespan of %v to table", key, lifeSpan) table.items[key] = item - // 取出需要的内容,释放锁 expDur := table.cleanupInterval addedItem := table.addedCallback table.Unlock() - // 添加回调函数 if addedItem != nil { addedItem(item) } - // 触发过期检查 if lifeSpan > 0 && (expDur == 0 || lifeSpan < expDur) { table.checkExpire() } return true } -//从缓存中返回一个被标记的并保持活性的值。你可以传附件的参数到DataLoader回调函数 func (table *Table) Value(key interface{}, args ...interface{}) (*Item, error) { table.RLock() r, ok := table.items[key] @@ -320,12 +292,11 @@ func (table *Table) Value(key interface{}, args ...interface{}) (*Item, error) { table.RUnlock() if ok { - // 更新访问次数和访问时间 + //update visit count and visit time. r.KeepAlive() return r, nil } - // 有加载数据的方式,就通过loadData函数去加载进来 if loadData != nil { item := loadData(key, args...) if item != nil { @@ -333,14 +304,13 @@ func (table *Table) Value(key interface{}, args ...interface{}) (*Item, error) { return item, nil } - return nil, errors.New("无法加载到缓存值") + return nil, errors.New("cannot load item") } - //没有找到任何东西,返回nil. return nil, nil } -// 删除缓存表中的所有项目 +// truncate a table. func (table *Table) Truncate() { table.Lock() defer table.Unlock() @@ -354,7 +324,7 @@ func (table *Table) Truncate() { } } -//辅助table中排序,统计的 +//support table sort type ItemPair struct { Key interface{} AccessCount int64 @@ -366,7 +336,7 @@ func (p ItemPairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] } func (p ItemPairList) Len() int { return len(p) } func (p ItemPairList) Less(i, j int) bool { return p[i].AccessCount > p[j].AccessCount } -// 返回缓存表中被访问最多的项目 +//return most visited. func (table *Table) MostAccessed(count int64) []*Item { table.RLock() defer table.RUnlock() @@ -396,12 +366,11 @@ func (table *Table) MostAccessed(count int64) []*Item { return r } -// 打印日志 +// print log. func (table *Table) log(format string, v ...interface{}) { - //core.LOGGER.Info(format, v...) + fmt.Printf(format, v) } -//新建一个缓存Table func NewTable() *Table { return &Table{ items: make(map[interface{}]*Item), diff --git a/code/tool/download/download.go b/code/tool/download/download.go index 774fb3a..fcd55db 100644 --- a/code/tool/download/download.go +++ b/code/tool/download/download.go @@ -60,7 +60,7 @@ func CheckLastModified(w http.ResponseWriter, r *http.Request, modifyTime time.T return false } -// 处理ETag标签 +// handle ETag // CheckETag implements If-None-Match and If-Range checks. // // The ETag or modtime must have been previously set in the @@ -211,8 +211,7 @@ func PanicError(err error) { } } -//文件下载。具有进度功能。 -//下载功能参考:https://github.com/Masterminds/go-fileserver +//file download. https://github.com/Masterminds/go-fileserver func DownloadFile( writer http.ResponseWriter, request *http.Request, @@ -228,16 +227,15 @@ func DownloadFile( PanicError(e) }() - //根据参数添加content-disposition。该Header会让浏览器自动下载,而不是预览。 + //content-disposition tell browser to download rather than preview. if withContentDisposition { fileName := url.QueryEscape(filename) writer.Header().Set("content-disposition", "attachment; filename=\""+fileName+"\"") } - //显示文件大小。 fileInfo, err := diskFile.Stat() if err != nil { - panic("无法从磁盘中获取文件信息") + panic("cannot load fileInfo from disk." + filePath) } modifyTime := fileInfo.ModTime() @@ -261,7 +259,7 @@ func DownloadFile( ctypes, haveType := writer.Header()["Content-Type"] var ctype string if !haveType { - //使用mimeUtil来获取mime + //get mime ctype = util.GetFallbackMimeType(filename, "") if ctype == "" { // read a chunk to decide between utf-8 text and binary @@ -270,7 +268,7 @@ func DownloadFile( ctype = http.DetectContentType(buf[:n]) _, err := diskFile.Seek(0, os.SEEK_SET) // rewind to output whole file if err != nil { - panic("无法准确定位文件") + panic("cannot seek file") } } writer.Header().Set("Content-Type", ctype) diff --git a/code/tool/i18n/i18n.go b/code/tool/i18n/i18n.go index 2bb3eeb..828a8a4 100644 --- a/code/tool/i18n/i18n.go +++ b/code/tool/i18n/i18n.go @@ -26,7 +26,7 @@ var ( UsernameExist = &Item{English: `username "%s" exists`, Chinese: `用户名"%s"已存在`} UsernameNotExist = &Item{English: `username "%s" not exists`, Chinese: `用户名"%s"不存在`} UsernameIsNotAdmin = &Item{English: `username "%s" is not admin user`, Chinese: `用户名"%s"不是管理员账号`} - UsernameError = &Item{English: `username can only be letters, numbers or _`, Chinese: `用户名必填,且只能包含字母,数字和'_'`} + UsernameError = &Item{English: `username can only be lowercase letters, numbers or _`, Chinese: `用户名必填,且只能包含小写字母,数字和'_'`} UserRegisterNotAllowd = &Item{English: `admin has banned register`, Chinese: `管理员已禁用自主注册`} UserPasswordLengthError = &Item{English: `password at least 6 chars`, Chinese: `密码长度至少为6位`} UserOldPasswordError = &Item{English: `old password error`, Chinese: `旧密码不正确`} @@ -36,10 +36,13 @@ var ( MatterNameLengthExceedLimit = &Item{English: `filename's length exceed the limit %d > %d`, Chinese: `文件名称长度超过限制 %d > %d `} MatterSelectNumExceedLimit = &Item{English: `selected files' num exceed the limit %d > %d`, Chinese: `选择的文件数量超出限制了 %d > %d `} MatterSelectSizeExceedLimit = &Item{English: `selected files' size exceed the limit %s > %s`, Chinese: `选择的文件大小超出限制了 %s > %s `} + MatterSizeExceedLimit = &Item{English: `uploaded file's size exceed the size limit %s > %s `, Chinese: `上传的文件超过了限制 %s > %s `} MatterNameContainSpecialChars = &Item{English: `file name cannot contain special chars \ / : * ? " < > |"`, Chinese: `名称中不能包含以下特殊符号:\ / : * ? " < > |`} MatterMoveRecursive = &Item{English: `directory cannot be moved to itself or its children`, Chinese: `文件夹不能把自己移入到自己中,也不可以移入到自己的子文件夹下。`} MatterNameNoChange = &Item{English: `filename not change, invalid operation`, Chinese: `文件名没有改变,操作无效!`} ShareNumExceedLimit = &Item{English: `sharing files' num exceed the limit %d > %d`, Chinese: `一次分享的文件数量超出限制了 %d > %d `} + ShareCodeRequired = &Item{English: `share code required`, Chinese: `提取码必填`} + ShareCodeError = &Item{English: `share code error`, Chinese: `提取码错误`} ) func (this *Item) Message(request *http.Request) string { diff --git a/code/tool/result/web_result.go b/code/tool/result/web_result.go index e9c11e4..d989862 100644 --- a/code/tool/result/web_result.go +++ b/code/tool/result/web_result.go @@ -37,7 +37,6 @@ var ( UNKNOWN = &CodeWrapper{Code: "UNKNOWN", HttpStatus: http.StatusInternalServerError, Description: "server unknow error"} ) -//根据 CodeWrapper来获取对应的HttpStatus func FetchHttpStatus(code string) int { if code == OK.Code { return OK.HttpStatus @@ -75,6 +74,12 @@ func ConstWebResult(codeWrapper *CodeWrapper) *WebResult { return wr } +func CustomWebResultI18n(request *http.Request, codeWrapper *CodeWrapper, item *i18n.Item, v ...interface{}) *WebResult { + + return CustomWebResult(codeWrapper, fmt.Sprintf(item.Message(request), v...)) + +} + func CustomWebResult(codeWrapper *CodeWrapper, description string) *WebResult { if description == "" { @@ -91,28 +96,25 @@ func BadRequestI18n(request *http.Request, item *i18n.Item, v ...interface{}) *W return CustomWebResult(BAD_REQUEST, fmt.Sprintf(item.Message(request), v...)) } -//没有权限 func BadRequest(format string, v ...interface{}) *WebResult { return CustomWebResult(BAD_REQUEST, fmt.Sprintf(format, v...)) } -//没有权限 func Unauthorized(format string, v ...interface{}) *WebResult { return CustomWebResult(UNAUTHORIZED, fmt.Sprintf(format, v...)) } -//没有找到 func NotFound(format string, v ...interface{}) *WebResult { return CustomWebResult(NOT_FOUND, fmt.Sprintf(format, v...)) } -//服务器内部出问题 +//sever inner error func Server(format string, v ...interface{}) *WebResult { return CustomWebResult(SERVER, fmt.Sprintf(format, v...)) } -//所有的数据库错误情况 +//db error. var ( DB_ERROR_DUPLICATE_KEY = "Error 1062: Duplicate entry" DB_ERROR_NOT_FOUND = "record not found" diff --git a/code/tool/util/util_encode.go b/code/tool/util/util_encode.go index 44d14b0..732f121 100644 --- a/code/tool/util/util_encode.go +++ b/code/tool/util/util_encode.go @@ -6,7 +6,7 @@ import ( "golang.org/x/crypto/bcrypt" ) -//给密码字符串加密 +//md5 func GetMd5(raw string) string { return fmt.Sprintf("%x", md5.Sum([]byte(raw))) } diff --git a/code/tool/util/util_env.go b/code/tool/util/util_env.go index 0b9492a..f44409a 100644 --- a/code/tool/util/util_env.go +++ b/code/tool/util/util_env.go @@ -6,7 +6,7 @@ import ( "strings" ) -//是否为win开发环境 +//whether windows develop environment func EnvWinDevelopment() bool { ex, err := os.Executable() @@ -14,7 +14,7 @@ func EnvWinDevelopment() bool { panic(err) } - //如果exPath中包含了 \\AppData\\Local\\Temp 我们认为是在Win的开发环境中 + //if exPath contains \\AppData\\Local\\Temp we regard as dev. systemUser, err := user.Current() if systemUser != nil { @@ -26,7 +26,7 @@ func EnvWinDevelopment() bool { } -//是否为mac开发环境 +//whether mac develop environment func EnvMacDevelopment() bool { ex, err := os.Executable() @@ -38,7 +38,7 @@ func EnvMacDevelopment() bool { } -//是否为开发环境 (即是否在IDE中运行) +//whether develop environment (whether run in IDE) func EnvDevelopment() bool { return EnvWinDevelopment() || EnvMacDevelopment() diff --git a/code/tool/util/util_file.go b/code/tool/util/util_file.go index 794a07d..1dca8af 100644 --- a/code/tool/util/util_file.go +++ b/code/tool/util/util_file.go @@ -7,12 +7,12 @@ import ( "io" "io/ioutil" "os" + "path" "path/filepath" "runtime" "strings" ) -//判断文件或文件夹是否已经存在 func PathExists(path string) bool { _, err := os.Stat(path) if err == nil { @@ -26,14 +26,13 @@ func PathExists(path string) bool { } } -//获取GOPATH路径 func GetGoPath() string { return build.Default.GOPATH } -//获取开发时的Home目录 +//get development home path. func GetDevHomePath() string { _, file, _, ok := runtime.Caller(0) @@ -50,8 +49,7 @@ func GetDevHomePath() string { return dir } -//获取该应用可执行文件的位置。 -//例如:C:\Users\lishuang\AppData\Local\Temp +//get home path for application. func GetHomePath() string { ex, err := os.Executable() if err != nil { @@ -70,8 +68,9 @@ func GetHomePath() string { return UniformPath(exPath) } -//获取前端静态资源的位置。如果你在开发模式下,可以将这里直接返回tank/build下面的html路径。 -//例如:C:/Users/lishuang/AppData/Local/Temp/html +//get html path +//dev: return $project/build/html +//prod: return $application/html func GetHtmlPath() string { //开发环境直接使用 build/html 下面的文件 @@ -81,27 +80,27 @@ func GetHtmlPath() string { return GetHomePath() + "/html" } -//如果文件夹存在就不管,不存在就创建。 例如:/var/www/matter +//if directory not exit, create it. func MakeDirAll(dirPath string) string { exists := PathExists(dirPath) if !exists { - //TODO:文件权限需要进一步考虑 + err := os.MkdirAll(dirPath, 0777) if err != nil { - panic("创建文件夹时出错!") + panic("error while creating directory") } } return dirPath } -//获取到一个Path的文件夹路径,eg /var/www/xx.log -> /var/www +//eg /var/www/xx.log -> /var/www func GetDirOfPath(fullPath string) string { index1 := strings.LastIndex(fullPath, "/") - //可能是windows的环境 + //maybe windows environment index2 := strings.LastIndex(fullPath, "\\") index := index1 if index2 > index1 { @@ -111,11 +110,11 @@ func GetDirOfPath(fullPath string) string { return fullPath[:index] } -//获取到一个Path 中的文件名,eg /var/www/xx.log -> xx.log +//get filename from path. eg /var/www/xx.log -> xx.log func GetFilenameOfPath(fullPath string) string { index1 := strings.LastIndex(fullPath, "/") - //可能是windows的环境 + //maybe windows env index2 := strings.LastIndex(fullPath, "\\") index := index1 if index2 > index1 { @@ -125,17 +124,17 @@ func GetFilenameOfPath(fullPath string) string { return fullPath[index+1:] } -//尝试删除空文件夹 true表示删掉了一个空文件夹,false表示没有删掉任何东西 +//try to delete empty dir. true: delete an empty dir, false: delete nothing. func DeleteEmptyDir(dirPath string) bool { dir, err := ioutil.ReadDir(dirPath) if err != nil { - panic(result.BadRequest("尝试读取目录%s时出错 %s", dirPath, err.Error())) + panic(result.BadRequest("occur error while reading %s %s", dirPath, err.Error())) } if len(dir) == 0 { - //空文件夹 + //empty dir err = os.Remove(dirPath) if err != nil { - panic(result.BadRequest("删除磁盘上的文件夹%s出错 %s", dirPath, err.Error())) + panic(result.BadRequest("occur error while deleting %s %s", dirPath, err.Error())) } return true } @@ -143,39 +142,23 @@ func DeleteEmptyDir(dirPath string) bool { return false } -//递归尝试删除空文件夹,一直空就一直删,直到不空为止 +//delete empty dir recursive, delete until not empty. func DeleteEmptyDirRecursive(dirPath string) { - fmt.Printf("递归删除删 %v \n", dirPath) + fmt.Printf("recursive delete %v \n", dirPath) tmpPath := dirPath for DeleteEmptyDir(tmpPath) { dir := GetDirOfPath(tmpPath) - fmt.Printf("尝试删除 %v\n", dir) + fmt.Printf("try to delete %v\n", dir) tmpPath = dir } } -//移除某个文件夹。 -func RemoveDirectory(dirPath string) string { - - exists := PathExists(dirPath) - if exists { - - err := os.Remove(dirPath) - if err != nil { - panic("删除文件夹时出错!") - } - } - - return dirPath -} - -//获取配置文件存放的位置 -//例如:C:\Users\lishuang\AppData\Local\Temp/conf +//get conf path. func GetConfPath() string { homePath := GetHomePath() @@ -185,15 +168,14 @@ func GetConfPath() string { if !exists { err := os.MkdirAll(filePath, 0777) if err != nil { - panic("创建日志文件夹时出错!") + panic("error while mkdir " + err.Error()) } } return filePath } -//获取日志的路径 -//例如:默认存放于 home/log +//get log path. func GetLogPath() string { homePath := GetHomePath() @@ -203,14 +185,14 @@ func GetLogPath() string { if !exists { err := os.MkdirAll(filePath, 0777) if err != nil { - panic("创建日志文件夹时出错!") + panic("error while mkdir " + err.Error()) } } return filePath } -//复制文件 +//copy file func CopyFile(srcPath string, destPath string) (nBytes int64) { srcFileStat, err := os.Stat(srcPath) @@ -248,9 +230,12 @@ func CopyFile(srcPath string, destPath string) (nBytes int64) { return nBytes } -//路径归一化处理,把\\替换成/ -func UniformPath(path string) string { - - return strings.Replace(path, "\\", "/", -1) - +//1. replace \\ to / +//2. clean path. +//3. trim suffix / +func UniformPath(p string) string { + p = strings.Replace(p, "\\", "/", -1) + p = path.Clean(p) + p = strings.TrimSuffix(p, "/") + return p } diff --git a/code/tool/util/util_mime.go b/code/tool/util/util_mime.go index 2f6ea7e..03a0606 100644 --- a/code/tool/util/util_mime.go +++ b/code/tool/util/util_mime.go @@ -618,7 +618,7 @@ var allMimeMap = map[string]string{ ".z": "application/x-compress", ".zip": "application/zip"} -//根据文件名字获取后缀名,均是小写。 +//get extension by name. lowercase. func GetExtension(filename string) string { var extension = filepath.Ext(filename) @@ -627,7 +627,7 @@ func GetExtension(filename string) string { } -//根据文件名字获取去除后缀的名称 +//get filename without extension func GetSimpleFileName(filename string) string { for i := len(filename) - 1; i >= 0 && !os.IsPathSeparator(filename[i]); i-- { @@ -639,7 +639,7 @@ func GetSimpleFileName(filename string) string { } -//根据一个后缀名获取MimeType,获取不到默认返回 "application/octet-stream" +//get MimeType by filename, if not found return "application/octet-stream" func GetMimeType(filename string) string { extension := GetExtension(filename) @@ -651,7 +651,7 @@ func GetMimeType(filename string) string { } } -//根据一个后缀名获取MimeType,获取不到默认返回fallback. +//get MimeType by filename, if not found return fallback. func GetFallbackMimeType(filename string, fallback string) string { extension := GetExtension(filename) diff --git a/code/tool/util/util_network.go b/code/tool/util/util_network.go index 5102ad0..1b3f436 100644 --- a/code/tool/util/util_network.go +++ b/code/tool/util/util_network.go @@ -5,7 +5,7 @@ import ( "strings" ) -//根据一个请求,获取ip. +//get ip from request func GetIpAddress(r *http.Request) string { var ipAddress string @@ -25,21 +25,21 @@ func GetIpAddress(r *http.Request) string { return ipAddress } -//根据一个请求,获取host -func GetHostFromRequest(r *http.Request) string { +//get host from request +func GetHostFromRequest(request *http.Request) string { - return r.Host + return request.Host } -//根据一个请求,获取authenticationId +//get cookieAuthKey from request. func GetSessionUuidFromRequest(request *http.Request, cookieAuthKey string) string { - //验证用户是否已经登录。 + //get from cookie sessionCookie, err := request.Cookie(cookieAuthKey) var sessionId string if err != nil { - //从入参中捞取 + //try to get from Form sessionId = request.FormValue(cookieAuthKey) } else { sessionId = sessionCookie.Value @@ -49,7 +49,7 @@ func GetSessionUuidFromRequest(request *http.Request, cookieAuthKey string) stri } -//允许跨域请求 +//allow cors. func AllowCORS(writer http.ResponseWriter) { writer.Header().Add("Access-Control-Allow-Origin", "*") writer.Header().Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE") @@ -57,9 +57,9 @@ func AllowCORS(writer http.ResponseWriter) { writer.Header().Add("Access-Control-Allow-Headers", "content-type") } -//禁用缓存 +//disable cache. func DisableCache(writer http.ResponseWriter) { - //对于IE浏览器,会自动缓存,因此设置不缓存Header. + //IE browser will cache automatically. disable the cache. writer.Header().Set("Pragma", "No-cache") writer.Header().Set("Cache-Control", "no-cache") writer.Header().Set("Expires", "0") diff --git a/code/tool/util/util_string.go b/code/tool/util/util_string.go index b4e4a27..ed33275 100644 --- a/code/tool/util/util_string.go +++ b/code/tool/util/util_string.go @@ -7,7 +7,6 @@ import ( "time" ) -//把一个大小转变成方便读的格式 //human readable file size func HumanFileSize(bytes int64) string { var thresh int64 = 1024 @@ -33,7 +32,7 @@ func HumanFileSize(bytes int64) string { return fmt.Sprintf("%s%s", numStr, units[u]) } -//获取MySQL的URL +//get mysql url. func GetMysqlUrl( mysqlPort int, mysqlHost string, @@ -43,15 +42,15 @@ func GetMysqlUrl( return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", mysqlUsername, mysqlPassword, mysqlHost, mysqlPort, mysqlSchema) } -//获取四位随机数字 +//get random number 4. func RandomNumber4() string { return fmt.Sprintf("%04v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31()%10000) } -//获取四位随机数字 +//get random 4 string func RandomString4() string { - //0和o,l和1难以区分,剔除掉 + //0 and o, 1 and l are not easy to distinguish var letterRunes = []rune("abcdefghijkmnpqrstuvwxyz23456789") r := rand.New(rand.NewSource(time.Now().UnixNano())) diff --git a/code/tool/util/util_time.go b/code/tool/util/util_time.go index 9ff70b7..144c036 100644 --- a/code/tool/util/util_time.go +++ b/code/tool/util/util_time.go @@ -5,57 +5,54 @@ import ( "time" ) -//将一个时间字符串转换成时间对象(yyyy-MM-dd HH:mm:ss) +//convert time string(yyyy-MM-dd HH:mm:ss) to Time object func ConvertDateTimeStringToTime(timeString string) time.Time { local, _ := time.LoadLocation("Local") t, err := time.ParseInLocation("2006-01-02 15:04:05", timeString, local) if err != nil { - panic(fmt.Sprintf("不能将%s转为时间类型", timeString)) + panic(fmt.Sprintf("cannot convert %s to Time", timeString)) } return t } -//将一个时间字符串转换成日期时间对象(yyyy-MM-dd HH:mm:ss) +//convert Time object to string(yyyy-MM-dd HH:mm:ss) func ConvertTimeToDateTimeString(time time.Time) string { return time.Local().Format("2006-01-02 15:04:05") } -//将一个时间字符串转换成日期时间对象(yyyy-MM-dd HH:mm:ss) +//convert Time object to string(HH:mm:ss) func ConvertTimeToTimeString(time time.Time) string { return time.Local().Format("15:04:05") } -//将一个时间字符串转换成日期对象(yyyy-MM-dd) +//convert Time object to string(yyyy-MM-dd) func ConvertTimeToDateString(time time.Time) string { return time.Local().Format("2006-01-02") } -//一天中的最后一秒钟 func LastSecondOfDay(day time.Time) time.Time { local, _ := time.LoadLocation("Local") return time.Date(day.Year(), day.Month(), day.Day(), 23, 59, 59, 0, local) } -//一天中的第一秒钟 func FirstSecondOfDay(day time.Time) time.Time { local, _ := time.LoadLocation("Local") return time.Date(day.Year(), day.Month(), day.Day(), 0, 0, 0, 0, local) } -//一天中的第一分钟 func FirstMinuteOfDay(day time.Time) time.Time { local, _ := time.LoadLocation("Local") return time.Date(day.Year(), day.Month(), day.Day(), 0, 1, 0, 0, local) } -//明天此刻的时间 +//Tomorrow right now func Tomorrow() time.Time { tomorrow := time.Now() tomorrow = tomorrow.AddDate(0, 0, 1) return tomorrow } -//昨天此刻的时间 +//Yesterday right now func Yesterday() time.Time { tomorrow := time.Now() tomorrow = tomorrow.AddDate(0, 0, -1) diff --git a/code/tool/util/util_zip.go b/code/tool/util/util_zip.go index 2877462..54199f8 100644 --- a/code/tool/util/util_zip.go +++ b/code/tool/util/util_zip.go @@ -4,23 +4,20 @@ import ( "archive/zip" "github.com/eyebluecn/tank/code/tool/result" "io" - "log" "os" "path/filepath" "strings" ) -//将srcPath压缩到destPath。 +//zip srcPath to destPath。 func Zip(srcPath string, destPath string) error { - //统一处理\\成/ srcPath = UniformPath(srcPath) if PathExists(destPath) { - panic(result.BadRequest("%s 已经存在了", destPath)) + panic(result.BadRequest("%s exits", destPath)) } - // 创建准备写入的文件 fileWriter, err := os.Create(destPath) if err != nil { return err @@ -32,53 +29,44 @@ func Zip(srcPath string, destPath string) error { } }() - // 通过 fileWriter 来创建 zip.Write zipWriter := zip.NewWriter(fileWriter) defer func() { - // 检测一下是否成功关闭 + if err := zipWriter.Close(); err != nil { - log.Fatalln(err) + panic(err) } }() - //上一个文件夹路径 baseDirPath := GetDirOfPath(srcPath) + "/" - // 下面来将文件写入 zipWriter ,因为有可能会有很多个目录及文件,所以递归处理 err = filepath.Walk(srcPath, func(path string, fileInfo os.FileInfo, errBack error) (err error) { if errBack != nil { return errBack } - //统一处理\\成/ path = UniformPath(path) - // 通过文件信息,创建 zip 的文件信息 fileHeader, err := zip.FileInfoHeader(fileInfo) if err != nil { return } - // 替换文件信息中的文件名 fileHeader.Name = strings.TrimPrefix(path, baseDirPath) - // 目录前要加上/ + // directory need / if fileInfo.IsDir() { fileHeader.Name += "/" } - // 写入文件信息,并返回一个 Write 结构 writer, err := zipWriter.CreateHeader(fileHeader) if err != nil { return } - // 检测,如果不是标准文件就只写入头信息,不写入文件数据到 writer - // 如目录,也没有数据需要写 + //only regular has things to write. if !fileHeader.Mode().IsRegular() { return nil } - // 打开要压缩的文件 fileToBeZip, err := os.Open(path) defer func() { err = fileToBeZip.Close() @@ -90,7 +78,6 @@ func Zip(srcPath string, destPath string) error { return } - // 将打开的文件 Copy 到 writer _, err = io.Copy(writer, fileToBeZip) if err != nil { return