diff --git a/Deploy/db/registry.sql b/Deploy/db/registry.sql index 9e4a342b3..bd0644b33 100644 --- a/Deploy/db/registry.sql +++ b/Deploy/db/registry.sql @@ -94,6 +94,7 @@ create table access_log ( user_id int NOT NULL, project_id int NOT NULL, repo_name varchar (40), + repo_tag varchar (20), GUID varchar(64), operation varchar(20) NOT NULL, op_time timestamp, diff --git a/README.md b/README.md index 9f66f175a..06dbb8cf9 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Project Harbor is an enterprise-class registry server. It extends the open sourc * **Graphical user portal**: User can easily browse, search docker repositories, manage projects/namespaces. * **AD/LDAP support**: Harbor integrates with existing AD/LDAP of the enterprise for user authentication and management. * **Auditing**: All the operations to the repositories are tracked and can be used for auditing purpose. -* **Internationalization**: Localized for English and Chinese languages. More languages can be added. +* **Internationalization**: Localized for English, Chinese and German languages. More languages can be added. * **RESTful API**: RESTful APIs are provided for most administrative operations of Harbor. The integration with other management softwares becomes easy. ### Getting Started diff --git a/api/project.go b/api/project.go index 7e6f46e7e..993cc9ad7 100644 --- a/api/project.go +++ b/api/project.go @@ -185,6 +185,7 @@ func (p *ProjectAPI) FilterAccessLog() { p.CustomAbort(http.StatusInternalServerError, "Internal error.") } p.Data["json"] = accessLogList + p.ServeJSON() } diff --git a/api/repository.go b/api/repository.go index e4e4ea62b..72669eebc 100644 --- a/api/repository.go +++ b/api/repository.go @@ -168,18 +168,6 @@ type tag struct { Tags []string `json:"tags"` } -type histroyItem struct { - V1Compatibility string `json:"v1Compatibility"` -} - -type manifest struct { - Name string `json:"name"` - Tag string `json:"tag"` - Architecture string `json:"architecture"` - SchemaVersion int `json:"schemaVersion"` - History []histroyItem `json:"history"` -} - // GetTags handles GET /api/repositories/tags func (ra *RepositoryAPI) GetTags() { repoName := ra.GetString("repo_name") @@ -193,9 +181,8 @@ func (ra *RepositoryAPI) GetTags() { ra.CustomAbort(http.StatusInternalServerError, "internal error") } - tags := []string{} - - ts, err := rc.ListTag() + repoName := ra.GetString("repo_name") + tags, err := ra.registry.ListTag(repoName) if err != nil { e, ok := errors.ParseError(err) if ok { @@ -240,8 +227,7 @@ func (ra *RepositoryAPI) GetManifests() { ra.CustomAbort(http.StatusInternalServerError, "internal error") } } - - mani := manifest{} + mani := models.Manifest{} err = json.Unmarshal(payload, &mani) if err != nil { log.Errorf("Failed to decode json from response for manifests, repo name: %s, tag: %s, error: %v", repoName, tag, err) diff --git a/dao/accesslog.go b/dao/accesslog.go index 721ac92cd..dbf447353 100644 --- a/dao/accesslog.go +++ b/dao/accesslog.go @@ -19,6 +19,7 @@ import ( "strings" "github.com/vmware/harbor/models" + "github.com/vmware/harbor/utils/log" "github.com/astaxie/beego/orm" ) @@ -27,14 +28,14 @@ import ( func AddAccessLog(accessLog models.AccessLog) error { o := orm.NewOrm() p, err := o.Raw(`insert into access_log - (user_id, project_id, repo_name, guid, operation, op_time) - values (?, ?, ?, ?, ?, now())`).Prepare() + (user_id, project_id, repo_name, repo_tag, guid, operation, op_time) + values (?, ?, ?, ?, ?, ?, now())`).Prepare() if err != nil { return err } defer p.Close() - _, err = p.Exec(accessLog.UserID, accessLog.ProjectID, accessLog.RepoName, accessLog.GUID, accessLog.Operation) + _, err = p.Exec(accessLog.UserID, accessLog.ProjectID, accessLog.RepoName, accessLog.RepoTag, accessLog.GUID, accessLog.Operation) return err } @@ -43,7 +44,7 @@ func AddAccessLog(accessLog models.AccessLog) error { func GetAccessLogs(accessLog models.AccessLog) ([]models.AccessLog, error) { o := orm.NewOrm() - sql := `select a.log_id, u.username, a.repo_name, a.operation, a.op_time + sql := `select a.log_id, u.username, a.repo_name, a.repo_tag, a.operation, a.op_time from access_log a left join user u on a.user_id = u.user_id where a.project_id = ? ` queryParam := make([]interface{}, 1) @@ -96,12 +97,15 @@ func GetAccessLogs(accessLog models.AccessLog) ([]models.AccessLog, error) { } // AccessLog ... -func AccessLog(username, projectName, repoName, action string) error { +func AccessLog(username, projectName, repoName, repoTag, action string) error { o := orm.NewOrm() - sql := "insert into access_log (user_id, project_id, repo_name, operation, op_time) " + + sql := "insert into access_log (user_id, project_id, repo_name, repo_tag, operation, op_time) " + "select (select user_id as user_id from user where username=?), " + - "(select project_id as project_id from project where name=?), ?, ?, now() " - _, err := o.Raw(sql, username, projectName, repoName, action).Exec() + "(select project_id as project_id from project where name=?), ?, ?, ?, now() " + _, err := o.Raw(sql, username, projectName, repoName, repoTag, action).Exec() + if err != nil { + log.Errorf("error in AccessLog: %v ", err) + } return err } diff --git a/dao/project.go b/dao/project.go index 5a6152029..9eba4f78e 100644 --- a/dao/project.go +++ b/dao/project.go @@ -60,7 +60,7 @@ func AddProject(project models.Project) (int64, error) { return projectID, err } - accessLog := models.AccessLog{UserID: project.OwnerID, ProjectID: projectID, RepoName: project.Name + "/", GUID: "N/A", Operation: "create", OpTime: time.Now()} + accessLog := models.AccessLog{UserID: project.OwnerID, ProjectID: projectID, RepoName: project.Name + "/", RepoTag: "N/A", GUID: "N/A", Operation: "create", OpTime: time.Now()} err = AddAccessLog(accessLog) return projectID, err diff --git a/models/accesslog.go b/models/accesslog.go index 150f3b588..3111e3a44 100644 --- a/models/accesslog.go +++ b/models/accesslog.go @@ -25,6 +25,7 @@ type AccessLog struct { UserID int `orm:"column(user_id)" json:"UserId"` ProjectID int64 `orm:"column(project_id)" json:"ProjectId"` RepoName string `orm:"column(repo_name)"` + RepoTag string `orm:"column(repo_tag)"` GUID string `orm:"column(GUID)" json:"Guid"` Operation string `orm:"column(operation)"` OpTime time.Time `orm:"column(op_time)"` diff --git a/models/notification.go b/models/notification.go index 0f608e2c6..3f30ae7e4 100644 --- a/models/notification.go +++ b/models/notification.go @@ -40,6 +40,7 @@ type Target struct { Digest string Repository string URL string `json:"Url"` + Tag string } // Actor holds information about actor. diff --git a/models/repo.go b/models/repo.go index 6b8de742b..f224c902e 100644 --- a/models/repo.go +++ b/models/repo.go @@ -42,3 +42,21 @@ type Tag struct { Version string `json:"version"` ImageID string `json:"image_id"` } + +// Manifest ... +type Manifest struct { + SchemaVersion int `json:"schemaVersion"` + Name string `json:"name"` + Tag string `json:"tag"` + Architecture string `json:"architecture"` + FsLayers []blobSumItem `json:"fsLayers"` + History []histroyItem `json:"history"` +} + +type histroyItem struct { + V1Compatibility string `json:"v1Compatibility"` +} + +type blobSumItem struct { + BlobSum string `json:"blobSum"` +} diff --git a/service/notification.go b/service/notification.go index beab28778..913d45b04 100644 --- a/service/notification.go +++ b/service/notification.go @@ -46,7 +46,7 @@ func (n *NotificationHandler) Post() { log.Errorf("error while decoding json: %v", err) return } - var username, action, repo, project string + var username, action, repo, project, repoTag string var matched bool for _, e := range notification.Events { matched, err = regexp.MatchString(manifestPattern, e.Target.MediaType) @@ -58,13 +58,16 @@ func (n *NotificationHandler) Post() { username = e.Actor.Name action = e.Action repo = e.Target.Repository + repoTag = e.Target.Tag + log.Debugf("repo tag is : %v ", repoTag) + if strings.Contains(repo, "/") { project = repo[0:strings.LastIndex(repo, "/")] } if username == "" { username = "anonymous" } - go dao.AccessLog(username, project, repo, action) + go dao.AccessLog(username, project, repo, repoTag, action) if action == "push" { go func() { err2 := svc_utils.RefreshCatalogCache() diff --git a/static/i18n/locale_en-US.ini b/static/i18n/locale_en-US.ini index 7a51acb6c..30f7009e0 100644 --- a/static/i18n/locale_en-US.ini +++ b/static/i18n/locale_en-US.ini @@ -59,6 +59,7 @@ repo = Repositories user = Users logs = Logs repo_name = Repository Name +repo_tag = Tag add_members = Add Members operation = Operation advance = Advanced Search diff --git a/static/i18n/locale_zh-CN.ini b/static/i18n/locale_zh-CN.ini index 9ddc12724..7f92b463f 100644 --- a/static/i18n/locale_zh-CN.ini +++ b/static/i18n/locale_zh-CN.ini @@ -59,6 +59,7 @@ repo = 镜像仓库 user = 用户 logs = 日志 repo_name = 镜像名称 +repo_tag = 镜像标签 add_members = 添加成员 operation = 操作 advance = 高级检索 diff --git a/static/resources/js/item-detail.js b/static/resources/js/item-detail.js index 1b6ffa771..6f445f717 100644 --- a/static/resources/js/item-detail.js +++ b/static/resources/js/item-detail.js @@ -286,6 +286,7 @@ jQuery(function(){ '' + '' + e.Username + '' + '' + e.RepoName + '' + + '' + e.RepoTag + '' + '' + e.Operation + '' + '' + moment(new Date(e.OpTime)).format("YYYY-MM-DD HH:mm:ss") + '' + ''); diff --git a/views/item-detail.tpl b/views/item-detail.tpl index a1ce83e96..e9cdb2753 100644 --- a/views/item-detail.tpl +++ b/views/item-detail.tpl @@ -159,10 +159,11 @@ - - - - + + + + +
{{i18n .Lang "username"}}{{i18n .Lang "repo_name"}}{{i18n .Lang "operation"}}{{i18n .Lang "timestamp"}}{{i18n .Lang "username"}}{{i18n .Lang "repo_name"}}{{i18n .Lang "repo_tag"}}{{i18n .Lang "operation"}}{{i18n .Lang "timestamp"}}