diff --git a/api/project.go b/api/project.go index da26b8a0a..fdbce7591 100644 --- a/api/project.go +++ b/api/project.go @@ -18,7 +18,7 @@ package api import ( "fmt" "net/http" - "strings" + "regexp" "github.com/vmware/harbor/dao" "github.com/vmware/harbor/models" @@ -285,19 +285,13 @@ func isProjectAdmin(userID int, pid int64) bool { func validateProjectReq(req projectReq) error { pn := req.ProjectName - if len(pn) == 0 { - return fmt.Errorf("Project name can not be empty") - } if isIllegalLength(req.ProjectName, projectNameMinLen, projectNameMaxLen) { - return fmt.Errorf("project name is illegal in length. (greater than 4 or less than 30)") + return fmt.Errorf("Project name is illegal in length. (greater than 4 or less than 30)") } - if isContainIllegalChar(req.ProjectName, []string{"~", "-", "$", "\\", "[", "]", "{", "}", "(", ")", "&", "^", "%", "*", "<", ">", "\"", "'", "/", "?", "@"}) { - return fmt.Errorf("project name contains illegal characters") + validProjectName := regexp.MustCompile(`^[a-z0-9](?:-*[a-z0-9])*(?:[._][a-z0-9](?:-*[a-z0-9])*)*$`) + legal := validProjectName.MatchString(pn) + if !legal { + return fmt.Errorf("Project name is not in lower case or contains illegal characters!") } - - if pn != strings.ToLower(pn) { - return fmt.Errorf("project name must be in lower case") - } - return nil } diff --git a/controllers/password.go b/controllers/password.go index f285e64a9..b1dc3becc 100644 --- a/controllers/password.go +++ b/controllers/password.go @@ -59,7 +59,7 @@ func (cc *CommonController) SendEmail() { cc.CustomAbort(http.StatusInternalServerError, "Internal error.") } err = messageTemplate.Execute(message, messageDetail{ - Hint: cc.Tr("reset_email_hint"), + Hint: cc.Tr("Warning: You're receiving this because you're requesting for changing password in Harbor, if it is not your operation, please ignore; otherwise, please click the link below"), URL: harborURL, UUID: uuid, }) diff --git a/dao/accesslog.go b/dao/accesslog.go index b407acb37..930607701 100644 --- a/dao/accesslog.go +++ b/dao/accesslog.go @@ -150,14 +150,44 @@ func GetRecentLogs(userID, linesNum int, startTime, endTime string) ([]models.Ac func GetTopRepos(countNum int) ([]models.TopRepo, error) { o := GetOrmer() - - sql := "select repo_name, COUNT(repo_name) as access_count from access_log left join project on access_log.project_id=project.project_id where project.public = 1 and access_log.operation = 'pull' group by repo_name order by access_count desc limit ? " - queryParam := make([]interface{}, 1) + // hide the where condition: project.public = 1, Can add to the sql when necessary. + sql := "select repo_name, COUNT(repo_name) as access_count from access_log left join project on access_log.project_id=project.project_id where access_log.operation = 'pull' group by repo_name order by access_count desc limit ? " + queryParam := []interface{}{} queryParam = append(queryParam, countNum) - var lists []models.TopRepo - _, err := o.Raw(sql, queryParam).QueryRows(&lists) + var list []models.TopRepo + _, err := o.Raw(sql, queryParam).QueryRows(&list) if err != nil { return nil, err } - return lists, nil + if len(list) == 0 { + return list, nil + } + placeHolder := make([]string, len(list)) + repos := make([]string, len(list)) + for i, v := range list { + repos[i] = v.RepoName + placeHolder[i] = "?" + } + placeHolderStr := strings.Join(placeHolder, ",") + queryParam = nil + queryParam = append(queryParam, repos) + var usrnameList []models.TopRepo + sql = `select a.username as creator, a.repo_name from (select access_log.repo_name, user.username, + access_log.op_time from user left join access_log on user.user_id = access_log.user_id where + access_log.operation = 'push' and access_log.repo_name in (######) order by access_log.repo_name, + access_log.op_time ASC) a group by a.repo_name` + sql = strings.Replace(sql, "######", placeHolderStr, 1) + _, err = o.Raw(sql, queryParam).QueryRows(&usrnameList) + if err != nil { + return nil, err + } + for i := 0; i < len(list); i++ { + for _, v := range usrnameList { + if v.RepoName == list[i].RepoName { + list[i].Creator = v.Creator + break + } + } + } + return list, nil } diff --git a/models/toprepo.go b/models/toprepo.go index 14bcffa66..6ccb46227 100644 --- a/models/toprepo.go +++ b/models/toprepo.go @@ -19,4 +19,5 @@ package models type TopRepo struct { RepoName string `json:"name"` AccessCount int64 `json:"count"` + Creator string `json:"creator"` } diff --git a/ui/router.go b/ui/router.go index 39ad9bae6..4fe8a4184 100644 --- a/ui/router.go +++ b/ui/router.go @@ -81,8 +81,7 @@ func initRouters() { beego.Router("/api/targets/ping", &api.TargetAPI{}, "post:Ping") beego.Router("/api/users/:id/sysadmin", &api.UserAPI{}, "put:ToggleUserAdminRole") beego.Router("/api/repositories/top", &api.RepositoryAPI{}, "get:GetTopRepos") - beego.Router("api/logs", &api.LogAPI{}) - + beego.Router("/api/logs", &api.LogAPI{}) //external service that hosted on harbor process: beego.Router("/service/notifications", &service.NotificationHandler{}) beego.Router("/service/token", &token.Handler{})