mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-18 22:57:38 +01:00
Merge remote-tracking branch 'upstream/new-ui-with-sync-image' into job-service
This commit is contained in:
commit
9107de8744
@ -98,7 +98,8 @@ render(os.path.join(templates_dir, "ui", "env"),
|
|||||||
ldap_url=ldap_url,
|
ldap_url=ldap_url,
|
||||||
ldap_basedn=ldap_basedn,
|
ldap_basedn=ldap_basedn,
|
||||||
self_registration=self_registration,
|
self_registration=self_registration,
|
||||||
ui_secret=ui_secret)
|
ui_secret=ui_secret,
|
||||||
|
verify_remote_cert=verify_remote_cert)
|
||||||
|
|
||||||
render(os.path.join(templates_dir, "ui", "app.conf"),
|
render(os.path.join(templates_dir, "ui", "app.conf"),
|
||||||
ui_conf,
|
ui_conf,
|
||||||
|
@ -17,3 +17,4 @@ LOG_LEVEL=debug
|
|||||||
GODEBUG=netdns=cgo
|
GODEBUG=netdns=cgo
|
||||||
EXT_ENDPOINT=$ui_url
|
EXT_ENDPOINT=$ui_url
|
||||||
TOKEN_URL=http://ui
|
TOKEN_URL=http://ui
|
||||||
|
VERIFY_REMOTE_CERT=$verify_remote_cert
|
||||||
|
12
api/base.go
12
api/base.go
@ -19,6 +19,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/astaxie/beego/validation"
|
"github.com/astaxie/beego/validation"
|
||||||
@ -136,3 +137,14 @@ func (b *BaseAPI) GetIDFromURL() int64 {
|
|||||||
|
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getIsInsecure() bool {
|
||||||
|
insecure := false
|
||||||
|
|
||||||
|
verifyRemoteCert := os.Getenv("VERIFY_REMOTE_CERT")
|
||||||
|
if verifyRemoteCert == "off" {
|
||||||
|
insecure = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return insecure
|
||||||
|
}
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/vmware/harbor/dao"
|
"github.com/vmware/harbor/dao"
|
||||||
"github.com/vmware/harbor/models"
|
"github.com/vmware/harbor/models"
|
||||||
@ -59,6 +60,7 @@ func (ra *RepJobAPI) Prepare() {
|
|||||||
func (ra *RepJobAPI) List() {
|
func (ra *RepJobAPI) List() {
|
||||||
var policyID int64
|
var policyID int64
|
||||||
var repository, status string
|
var repository, status string
|
||||||
|
var startTime, endTime *time.Time
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
policyIDStr := ra.GetString("policy_id")
|
policyIDStr := ra.GetString("policy_id")
|
||||||
@ -69,10 +71,36 @@ func (ra *RepJobAPI) List() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
endTimeStr := ra.GetString("end_time")
|
||||||
|
if len(endTimeStr) != 0 {
|
||||||
|
i, err := strconv.ParseInt(endTimeStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
ra.CustomAbort(http.StatusBadRequest, "invalid end_time")
|
||||||
|
}
|
||||||
|
t := time.Unix(i, 0)
|
||||||
|
endTime = &t
|
||||||
|
}
|
||||||
|
|
||||||
|
startTimeStr := ra.GetString("start_time")
|
||||||
|
if len(startTimeStr) != 0 {
|
||||||
|
i, err := strconv.ParseInt(startTimeStr, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
ra.CustomAbort(http.StatusBadRequest, "invalid start_time")
|
||||||
|
}
|
||||||
|
t := time.Unix(i, 0)
|
||||||
|
startTime = &t
|
||||||
|
}
|
||||||
|
|
||||||
|
if startTime == nil && endTime == nil {
|
||||||
|
// if start_time and end_time are both null, list jobs of last 10 days
|
||||||
|
t := time.Now().UTC().AddDate(0, 0, -10)
|
||||||
|
startTime = &t
|
||||||
|
}
|
||||||
|
|
||||||
repository = ra.GetString("repository")
|
repository = ra.GetString("repository")
|
||||||
status = ra.GetString("status")
|
status = ra.GetString("status")
|
||||||
|
|
||||||
jobs, err := dao.FilterRepJobs(policyID, repository, status)
|
jobs, err := dao.FilterRepJobs(policyID, repository, status, startTime, endTime, 1000)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to filter jobs according policy ID %d, repository %s, status %s: %v", policyID, repository, status, err)
|
log.Errorf("failed to filter jobs according policy ID %d, repository %s, status %s: %v", policyID, repository, status, err)
|
||||||
ra.RenderError(http.StatusInternalServerError, "Failed to query job")
|
ra.RenderError(http.StatusInternalServerError, "Failed to query job")
|
||||||
|
@ -144,6 +144,18 @@ func (ra *RepositoryAPI) Delete() {
|
|||||||
tags = append(tags, tag)
|
tags = append(tags, tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
project := ""
|
||||||
|
if strings.Contains(repoName, "/") {
|
||||||
|
project = repoName[0:strings.LastIndex(repoName, "/")]
|
||||||
|
}
|
||||||
|
user, _, ok := ra.Ctx.Request.BasicAuth()
|
||||||
|
if !ok {
|
||||||
|
user, err = ra.getUsername()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to get user: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, t := range tags {
|
for _, t := range tags {
|
||||||
if err := rc.DeleteTag(t); err != nil {
|
if err := rc.DeleteTag(t); err != nil {
|
||||||
if regErr, ok := err.(*registry_error.Error); ok {
|
if regErr, ok := err.(*registry_error.Error); ok {
|
||||||
@ -156,6 +168,11 @@ func (ra *RepositoryAPI) Delete() {
|
|||||||
log.Infof("delete tag: %s %s", repoName, t)
|
log.Infof("delete tag: %s %s", repoName, t)
|
||||||
go TriggerReplicationByRepository(repoName, []string{t}, models.RepOpDelete)
|
go TriggerReplicationByRepository(repoName, []string{t}, models.RepOpDelete)
|
||||||
|
|
||||||
|
go func(tag string) {
|
||||||
|
if err := dao.AccessLog(user, project, repoName, tag, "delete"); err != nil {
|
||||||
|
log.Errorf("failed to add access log: %v", err)
|
||||||
|
}
|
||||||
|
}(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@ -164,7 +181,6 @@ func (ra *RepositoryAPI) Delete() {
|
|||||||
log.Errorf("error occurred while refresh catalog cache: %v", err)
|
log.Errorf("error occurred while refresh catalog cache: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type tag struct {
|
type tag struct {
|
||||||
@ -255,12 +271,10 @@ func (ra *RepositoryAPI) GetManifests() {
|
|||||||
|
|
||||||
func (ra *RepositoryAPI) initRepositoryClient(repoName string) (r *registry.Repository, err error) {
|
func (ra *RepositoryAPI) initRepositoryClient(repoName string) (r *registry.Repository, err error) {
|
||||||
endpoint := os.Getenv("REGISTRY_URL")
|
endpoint := os.Getenv("REGISTRY_URL")
|
||||||
// TODO read variable from config file
|
|
||||||
insecure := true
|
|
||||||
|
|
||||||
username, password, ok := ra.Ctx.Request.BasicAuth()
|
username, password, ok := ra.Ctx.Request.BasicAuth()
|
||||||
if ok {
|
if ok {
|
||||||
return newRepositoryClient(endpoint, insecure, username, password,
|
return newRepositoryClient(endpoint, getIsInsecure(), username, password,
|
||||||
repoName, "repository", repoName, "pull", "push", "*")
|
repoName, "repository", repoName, "pull", "push", "*")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +283,7 @@ func (ra *RepositoryAPI) initRepositoryClient(repoName string) (r *registry.Repo
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return cache.NewRepositoryClient(endpoint, insecure, username, repoName,
|
return cache.NewRepositoryClient(endpoint, getIsInsecure(), username, repoName,
|
||||||
"repository", repoName, "pull", "push", "*")
|
"repository", repoName, "pull", "push", "*")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,9 +92,7 @@ func (t *TargetAPI) Ping() {
|
|||||||
password = t.GetString("password")
|
password = t.GetString("password")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO read variable from config file
|
registry, err := newRegistryClient(endpoint, getIsInsecure(), username, password,
|
||||||
insecure := true
|
|
||||||
registry, err := newRegistryClient(endpoint, insecure, username, password,
|
|
||||||
"", "", "")
|
"", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// timeout, dns resolve error, connection refused, etc.
|
// timeout, dns resolve error, connection refused, etc.
|
||||||
|
@ -55,16 +55,23 @@ func (b *BaseController) Prepare() {
|
|||||||
b.SetSession("Lang", lang)
|
b.SetSession("Lang", lang)
|
||||||
lang = sessionLang.(string)
|
lang = sessionLang.(string)
|
||||||
} else {
|
} else {
|
||||||
lang = defaultLang
|
al := b.Ctx.Request.Header.Get("Accept-Language")
|
||||||
|
if len(al) > 4 {
|
||||||
|
al = al[:5] // Only compare first 5 letters.
|
||||||
|
if i18n.IsExist(al) {
|
||||||
|
lang = al
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
b.SetSession("Lang", lang)
|
|
||||||
|
|
||||||
if _, exist := supportLanguages[lang]; !exist { //Check if support the request language.
|
if _, exist := supportLanguages[lang]; !exist { //Check if support the request language.
|
||||||
lang = defaultLang //Set default language if not supported.
|
lang = defaultLang //Set default language if not supported.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
b.Ctx.SetCookie("language", lang, 0, "/")
|
||||||
|
b.SetSession("Lang", lang)
|
||||||
|
|
||||||
curLang := langType{
|
curLang := langType{
|
||||||
Lang: lang,
|
Lang: lang,
|
||||||
}
|
}
|
||||||
|
@ -1180,7 +1180,7 @@ func TestDeleteRepJob(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFilterRepJobs(t *testing.T) {
|
func TestFilterRepJobs(t *testing.T) {
|
||||||
jobs, err := FilterRepJobs(policyID, "", "")
|
jobs, err := FilterRepJobs(policyID, "", "", nil, nil, 1000)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error occured in FilterRepJobs: %v, policy ID: %d", err, policyID)
|
log.Errorf("Error occured in FilterRepJobs: %v, policy ID: %d", err, policyID)
|
||||||
return
|
return
|
||||||
|
@ -311,7 +311,8 @@ func GetRepJobByPolicy(policyID int64) ([]*models.RepJob, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FilterRepJobs filters jobs by repo and policy ID
|
// FilterRepJobs filters jobs by repo and policy ID
|
||||||
func FilterRepJobs(policyID int64, repository, status string) ([]*models.RepJob, error) {
|
func FilterRepJobs(policyID int64, repository, status string, startTime,
|
||||||
|
endTime *time.Time, limit int) ([]*models.RepJob, error) {
|
||||||
o := GetOrmer()
|
o := GetOrmer()
|
||||||
|
|
||||||
qs := o.QueryTable(new(models.RepJob))
|
qs := o.QueryTable(new(models.RepJob))
|
||||||
@ -324,6 +325,21 @@ func FilterRepJobs(policyID int64, repository, status string) ([]*models.RepJob,
|
|||||||
if len(status) != 0 {
|
if len(status) != 0 {
|
||||||
qs = qs.Filter("Status__icontains", status)
|
qs = qs.Filter("Status__icontains", status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if startTime != nil {
|
||||||
|
fmt.Printf("%v\n", startTime)
|
||||||
|
qs = qs.Filter("CreationTime__gte", startTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
if endTime != nil {
|
||||||
|
fmt.Printf("%v\n", endTime)
|
||||||
|
qs = qs.Filter("CreationTime__lte", endTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
if limit != 0 {
|
||||||
|
qs = qs.Limit(limit)
|
||||||
|
}
|
||||||
|
|
||||||
qs = qs.OrderBy("-CreationTime")
|
qs = qs.OrderBy("-CreationTime")
|
||||||
|
|
||||||
var jobs []*models.RepJob
|
var jobs []*models.RepJob
|
||||||
|
@ -39,55 +39,85 @@ const manifestPattern = `^application/vnd.docker.distribution.manifest.v\d\+json
|
|||||||
// Post handles POST request, and records audit log or refreshes cache based on event.
|
// Post handles POST request, and records audit log or refreshes cache based on event.
|
||||||
func (n *NotificationHandler) Post() {
|
func (n *NotificationHandler) Post() {
|
||||||
var notification models.Notification
|
var notification models.Notification
|
||||||
//log.Info("Notification Handler triggered!\n")
|
|
||||||
// log.Infof("request body in string: %s", string(n.Ctx.Input.CopyBody()))
|
|
||||||
err := json.Unmarshal(n.Ctx.Input.CopyBody(1<<32), ¬ification)
|
err := json.Unmarshal(n.Ctx.Input.CopyBody(1<<32), ¬ification)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("error while decoding json: %v", err)
|
log.Errorf("failed to decode notification: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var username, action, repo, project, repoTag string
|
|
||||||
var matched bool
|
events, err := filterEvents(¬ification)
|
||||||
for _, e := range notification.Events {
|
|
||||||
matched, err = regexp.MatchString(manifestPattern, e.Target.MediaType)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to match the media type against pattern, error: %v", err)
|
log.Errorf("failed to filter events: %v", err)
|
||||||
matched = false
|
|
||||||
}
|
|
||||||
if matched && (strings.HasPrefix(e.Request.UserAgent, "docker") ||
|
|
||||||
strings.ToLower(strings.TrimSpace(e.Request.UserAgent)) == "harbor-registry-client") {
|
|
||||||
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"
|
|
||||||
}
|
|
||||||
|
|
||||||
if action == "pull" && username == "job-service-user" {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
go dao.AccessLog(username, project, repo, repoTag, action)
|
for _, event := range events {
|
||||||
|
repository := event.Target.Repository
|
||||||
|
|
||||||
|
project := ""
|
||||||
|
if strings.Contains(repository, "/") {
|
||||||
|
project = repository[0:strings.LastIndex(repository, "/")]
|
||||||
|
}
|
||||||
|
|
||||||
|
tag := event.Target.Tag
|
||||||
|
action := event.Action
|
||||||
|
|
||||||
|
user := event.Actor.Name
|
||||||
|
if len(user) == 0 {
|
||||||
|
user = "anonymous"
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := dao.AccessLog(user, project, repository, tag, action); err != nil {
|
||||||
|
log.Errorf("failed to add access log: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
if action == "push" {
|
if action == "push" {
|
||||||
go func() {
|
go func() {
|
||||||
err2 := cache.RefreshCatalogCache()
|
if err := cache.RefreshCatalogCache(); err != nil {
|
||||||
if err2 != nil {
|
log.Errorf("failed to refresh cache: %v", err)
|
||||||
log.Errorf("Error happens when refreshing cache: %v", err2)
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
go api.TriggerReplicationByRepository(repo, []string{repoTag}, models.RepOpTransfer)
|
operation := ""
|
||||||
|
if action == "push" {
|
||||||
|
operation = models.RepOpTransfer
|
||||||
|
}
|
||||||
|
|
||||||
|
go api.TriggerReplicationByRepository(repository, []string{tag}, operation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func filterEvents(notification *models.Notification) ([]*models.Event, error) {
|
||||||
|
events := []*models.Event{}
|
||||||
|
|
||||||
|
for _, event := range notification.Events {
|
||||||
|
isManifest, err := regexp.MatchString(manifestPattern, event.Target.MediaType)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to match the media type against pattern: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isManifest {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
//pull and push manifest by docker-client
|
||||||
|
if strings.HasPrefix(event.Request.UserAgent, "docker") && (event.Action == "pull" || event.Action == "push") {
|
||||||
|
events = append(events, &event)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
//push manifest by docker-client or job-service
|
||||||
|
if strings.ToLower(strings.TrimSpace(event.Request.UserAgent)) == "harbor-registry-client" && event.Action == "push" {
|
||||||
|
events = append(events, &event)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return events, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render returns nil as it won't render any template.
|
// Render returns nil as it won't render any template.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
.up-section .up-table-pane {
|
.up-section .up-table-pane {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
height: 180px;
|
height: 220px;
|
||||||
margin-top: -10px;
|
margin-top: -10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,3 +74,7 @@
|
|||||||
.color-warning {
|
.color-warning {
|
||||||
color: #f0ad4e;
|
color: #f0ad4e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.label-custom {
|
||||||
|
margin: 0 5px 0 10px;
|
||||||
|
}
|
@ -53,7 +53,11 @@
|
|||||||
console.log('vm.projects is undefined, load public projects.');
|
console.log('vm.projects is undefined, load public projects.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(angular.isArray(vm.projects) && vm.projects.length > 0) {
|
||||||
vm.selectedProject = vm.projects[0];
|
vm.selectedProject = vm.projects[0];
|
||||||
|
}else{
|
||||||
|
$window.location.href = '/project';
|
||||||
|
}
|
||||||
|
|
||||||
if(getParameterByName('project_id', $location.absUrl())){
|
if(getParameterByName('project_id', $location.absUrl())){
|
||||||
angular.forEach(vm.projects, function(value, index) {
|
angular.forEach(vm.projects, function(value, index) {
|
||||||
@ -64,8 +68,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$location.search('project_id', vm.selectedProject.project_id);
|
$location.search('project_id', vm.selectedProject.project_id);
|
||||||
vm.checkProjectMember(vm.selectedProject.project_id);
|
|
||||||
|
|
||||||
|
vm.checkProjectMember(vm.selectedProject.project_id);
|
||||||
vm.resultCount = vm.projects.length;
|
vm.resultCount = vm.projects.length;
|
||||||
|
|
||||||
$scope.$watch('vm.filterInput', function(current, origin) {
|
$scope.$watch('vm.filterInput', function(current, origin) {
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
function addProjectMemberComplete(data, status, header) {
|
function addProjectMemberComplete(data, status, header) {
|
||||||
console.log('addProjectMemberComplete: status:' + status + ', data:' + data);
|
console.log('addProjectMemberComplete: status:' + status + ', data:' + data);
|
||||||
vm.reload();
|
vm.reload();
|
||||||
|
vm.isOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addProjectMemberFailed(data, status, headers) {
|
function addProjectMemberFailed(data, status, headers) {
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
$scope.$emit('addedSuccess', true);
|
$scope.$emit('addedSuccess', true);
|
||||||
vm.hasError = false;
|
vm.hasError = false;
|
||||||
vm.errorMessage = '';
|
vm.errorMessage = '';
|
||||||
|
vm.isOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addProjectFailed(data, status) {
|
function addProjectFailed(data, status) {
|
||||||
|
@ -92,7 +92,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="submit" class="btn btn-primary" id="btnOk" ng-click="form.$valid && vm.save(replication)">// 'ok' | tr //</button>
|
<button ng-show="vm.targetEditable" type="submit" class="btn btn-primary" ng-click="vm.save(replication)">// 'ok' | tr //</button>
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">// 'close' | tr //</button>
|
<button type="button" class="btn btn-default" data-dismiss="modal">// 'close' | tr //</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -69,7 +69,7 @@
|
|||||||
|
|
||||||
$scope.$watch('vm.targetId', function(current) {
|
$scope.$watch('vm.targetId', function(current) {
|
||||||
if(current) {
|
if(current) {
|
||||||
vm1.selection.id = current;
|
vm1.selection.id = current || vm.destinations[0].id;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -125,15 +125,19 @@
|
|||||||
function saveOrUpdateDestination() {
|
function saveOrUpdateDestination() {
|
||||||
|
|
||||||
var target = {
|
var target = {
|
||||||
|
'name' : vm1.name,
|
||||||
'endpoint': vm1.endpoint,
|
'endpoint': vm1.endpoint,
|
||||||
'username': vm1.username,
|
'username': vm1.username,
|
||||||
'password': vm1.password
|
'password': vm1.password
|
||||||
};
|
};
|
||||||
|
|
||||||
if(vm.checkedAddTarget){
|
if(vm.checkedAddTarget){
|
||||||
target.name = vm1.name;
|
|
||||||
CreateDestinationService(target.name, target.endpoint, target.username, target.password)
|
CreateDestinationService(target.name, target.endpoint, target.username, target.password)
|
||||||
.success(createDestinationSuccess)
|
.success(createDestinationSuccess)
|
||||||
.error(createDestinationFailed);
|
.error(createDestinationFailed);
|
||||||
|
}else{
|
||||||
|
vm.policy.targetId = vm1.selection.id;
|
||||||
|
saveOrUpdatePolicy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +158,10 @@
|
|||||||
|
|
||||||
function update(policy) {
|
function update(policy) {
|
||||||
vm.policy = policy;
|
vm.policy = policy;
|
||||||
saveOrUpdateDestination();
|
if(vm.targetEditable) {
|
||||||
|
vm.policy.targetId = vm1.selection.id;
|
||||||
|
saveOrUpdatePolicy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function pingDestination() {
|
function pingDestination() {
|
||||||
|
@ -28,18 +28,18 @@
|
|||||||
<table class="table table-pane">
|
<table class="table table-pane">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-if="vm.replicationPolicies.length == 0">
|
<tr ng-if="vm.replicationPolicies.length == 0">
|
||||||
<td colspan="7" height="100%" class="empty-hint" ><h3 class="text-muted">// 'no_replication_policies' | tr //</h3></td>
|
<td colspan="7" height="100%" class="empty-hint" ><h3 class="text-muted">// 'no_replication_policies_add_new' | tr //</h3></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr policy_id="//r.id//" ng-if="vm.replicationPolicies.length > 0" ng-repeat="r in vm.replicationPolicies" value="//vm.last = $last//">
|
<tr policy_id="//r.id//" ng-if="vm.replicationPolicies.length > 0" ng-repeat="r in vm.replicationPolicies" value="//vm.last = $last//">
|
||||||
<td width="10%">//r.name//</td>
|
<td width="10%">//r.name//</td>
|
||||||
<td width="18%">//r.description//</td>
|
<td width="18%" style="padding-left: 10px;">//r.description//</td>
|
||||||
<td width="18%">//r.target_name//</td>
|
<td width="18%" style="padding-left: 15px;">//r.target_name//</td>
|
||||||
<td width="18%">//r.start_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
|
<td width="18%" style="padding-left: 18px;">//r.start_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
|
||||||
<td width="14%" ng-switch on="//r.enabled//">
|
<td width="14%" style="padding-left: 18px;" ng-switch on="//r.enabled//">
|
||||||
<span ng-switch-when="1">// 'enabled' | tr //</span>
|
<span ng-switch-when="1">// 'enabled' | tr //</span>
|
||||||
<span ng-switch-when="0">// 'disabled' | tr //</span>
|
<span ng-switch-when="0">// 'disabled' | tr //</span>
|
||||||
</td>
|
</td>
|
||||||
<td width="15%">
|
<td width="15%" style="padding-left: 20px;">
|
||||||
<div class="display-inline-block" ng-switch on="//r.enabled//">
|
<div class="display-inline-block" ng-switch on="//r.enabled//">
|
||||||
<a href="javascript:void(0);" ng-click="vm.togglePolicy(r.id, 0)" title="// 'disabled' | tr //"><span ng-switch-when="1" class="glyphicon glyphicon-stop color-danger"></span></a>
|
<a href="javascript:void(0);" ng-click="vm.togglePolicy(r.id, 0)" title="// 'disabled' | tr //"><span ng-switch-when="1" class="glyphicon glyphicon-stop color-danger"></span></a>
|
||||||
<a href="javascript:void(0);" ng-click="vm.togglePolicy(r.id, 1)" title="// 'enabled' | tr //"><span ng-switch-when="0" class="glyphicon glyphicon-play color-success"></span></a>
|
<a href="javascript:void(0);" ng-click="vm.togglePolicy(r.id, 1)" title="// 'enabled' | tr //"><span ng-switch-when="0" class="glyphicon glyphicon-play color-success"></span></a>
|
||||||
@ -66,6 +66,30 @@
|
|||||||
<button class="btn btn-primary" type="button" ng-click="vm.searchReplicationJob()" ng-disabled="vm.refreshPending"><span class="glyphicon glyphicon-search"></span></button>
|
<button class="btn btn-primary" type="button" ng-click="vm.searchReplicationJob()" ng-disabled="vm.refreshPending"><span class="glyphicon glyphicon-search"></span></button>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="selStatus" class="control-label label-custom">// 'status' | tr //:</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<select class="form-control" id="selStatus" ng-options="st.value for st in vm.jobStatus() track by st.key" ng-model="vm.currentStatus" ng-change="vm.searchReplicationJob()"></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="fromDatePicker" class="control-label label-custom">// 'from' | tr //:</label>
|
||||||
|
<div class="input-group datetimepicker">
|
||||||
|
<input id="fromDatePicker" class="form-control" type="text" readonly="readonly" ng-model="vm.fromDate" ng-change="vm.pickUp({key:'fromDate', value: vm.fromDate})" size="10">
|
||||||
|
<span class="input-group-addon">
|
||||||
|
<a href="javascript:void(0);"><span class="glyphicon glyphicon-calendar"></span></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label for="toDatePicker" class="control-label label-custom">// 'to' | tr //:</label>
|
||||||
|
<div class="input-group datetimepicker">
|
||||||
|
<input id="toDatePicker" class="form-control" type="text" readonly="readonly" ng-model="vm.toDate" ng-change="vm.pickUp({key:'toDate', value: vm.toDate})" size="10">
|
||||||
|
<span class="input-group-addon">
|
||||||
|
<a href="javascript:void(0);"><span class="glyphicon glyphicon-calendar"></span></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<button type="button" class="btn btn-success" ng-click="vm.searchReplicationJob()"><span class="glyphicon glyphicon-refresh"></span></button>
|
<button type="button" class="btn btn-success" ng-click="vm.searchReplicationJob()"><span class="glyphicon glyphicon-refresh"></span></button>
|
||||||
</div>
|
</div>
|
||||||
@ -94,11 +118,13 @@
|
|||||||
<tr ng-if="vm.replicationJobs.length > 0" ng-repeat="r in vm.replicationJobs">
|
<tr ng-if="vm.replicationJobs.length > 0" ng-repeat="r in vm.replicationJobs">
|
||||||
<td width="15%">//r.repository//</td>
|
<td width="15%">//r.repository//</td>
|
||||||
<td width="12%">//r.operation//</td>
|
<td width="12%">//r.operation//</td>
|
||||||
<td width="18%">//r.creation_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
|
<td width="18%" style="padding-left: 15px;">//r.creation_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
|
||||||
<td width="18%">//r.creation_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
|
<td width="18%" style="padding-left: 15px;">//r.creation_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
|
||||||
<td width="18%">//r.update_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
|
<td width="18%" style="padding-left: 18px;">//r.update_time | dateL : 'YYYY-MM-DD HH:mm:ss'//</td>
|
||||||
<td width="9%">//r.status//</td>
|
<td width="9%" style="padding-left: 20px;">//r.status//</td>
|
||||||
<td width="10%" align="left"><a href="javascript:void(0);" ng-click="vm.downloadLog(r.id)" title="// 'download_log' | tr //"><span style="margin-left: 10px;" class="glyphicon glyphicon-file"></span></a></td>
|
<td width="10%" align="left">
|
||||||
|
<a ng-show="r.status != 'canceled' && r.status != 'pending'" href="javascript:void(0);" ng-click="vm.downloadLog(r.id)" title="// 'download_log' | tr //"><span style="margin-left: 10px;" class="glyphicon glyphicon-file"></span></a>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -4,11 +4,27 @@
|
|||||||
|
|
||||||
angular
|
angular
|
||||||
.module('harbor.replication')
|
.module('harbor.replication')
|
||||||
.directive('listReplication', listReplication);
|
.directive('listReplication', listReplication)
|
||||||
|
.factory('jobStatus', jobStatus);
|
||||||
|
|
||||||
ListReplicationController.$inject = ['$scope', 'getParameterByName', '$location', 'ListReplicationPolicyService', 'ToggleReplicationPolicyService', 'ListReplicationJobService', '$window', '$filter', 'trFilter'];
|
jobStatus.inject = ['$filter', 'trFilter'];
|
||||||
|
function jobStatus($filter, trFilter) {
|
||||||
|
return function() {
|
||||||
|
return [
|
||||||
|
{'key': 'all' , 'value': $filter('tr')('all')},
|
||||||
|
{'key': 'pending', 'value': $filter('tr')('pending')},
|
||||||
|
{'key': 'running', 'value': $filter('tr')('running')},
|
||||||
|
{'key': 'error' , 'value': $filter('tr')('error')},
|
||||||
|
{'key': 'stopped', 'value': $filter('tr')('stopped')},
|
||||||
|
{'key': 'finished', 'value':$filter('tr')('finished')},
|
||||||
|
{'key': 'canceled', 'value': $filter('tr')('canceled')}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function ListReplicationController($scope, getParameterByName, $location, ListReplicationPolicyService, ToggleReplicationPolicyService, ListReplicationJobService, $window, $filter, trFilter) {
|
ListReplicationController.$inject = ['$scope', 'getParameterByName', '$location', 'ListReplicationPolicyService', 'ToggleReplicationPolicyService', 'ListReplicationJobService', '$window', '$filter', 'trFilter', 'jobStatus'];
|
||||||
|
|
||||||
|
function ListReplicationController($scope, getParameterByName, $location, ListReplicationPolicyService, ToggleReplicationPolicyService, ListReplicationJobService, $window, $filter, trFilter, jobStatus) {
|
||||||
var vm = this;
|
var vm = this;
|
||||||
|
|
||||||
vm.sectionHeight = {'min-height': '1200px'};
|
vm.sectionHeight = {'min-height': '1200px'};
|
||||||
@ -36,6 +52,9 @@
|
|||||||
vm.retrievePolicy();
|
vm.retrievePolicy();
|
||||||
vm.refreshPending = false;
|
vm.refreshPending = false;
|
||||||
|
|
||||||
|
vm.jobStatus = jobStatus;
|
||||||
|
vm.currentStatus = vm.jobStatus()[0];
|
||||||
|
|
||||||
function searchReplicationPolicy() {
|
function searchReplicationPolicy() {
|
||||||
vm.refreshPending = true;
|
vm.refreshPending = true;
|
||||||
vm.retrievePolicy();
|
vm.retrievePolicy();
|
||||||
@ -54,7 +73,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function retrieveJob(policyId) {
|
function retrieveJob(policyId) {
|
||||||
ListReplicationJobService(policyId, vm.replicationJobName)
|
var status = (vm.currentStatus.key === 'all' ? '' : vm.currentStatus.key);
|
||||||
|
ListReplicationJobService(policyId, vm.replicationJobName, status)
|
||||||
.success(listReplicationJobSuccess)
|
.success(listReplicationJobSuccess)
|
||||||
.error(listReplicationJobFailed);
|
.error(listReplicationJobFailed);
|
||||||
}
|
}
|
||||||
@ -70,6 +90,18 @@
|
|||||||
|
|
||||||
function listReplicationJobSuccess(data, status) {
|
function listReplicationJobSuccess(data, status) {
|
||||||
vm.replicationJobs = data || [];
|
vm.replicationJobs = data || [];
|
||||||
|
angular.forEach(vm.replicationJobs, function(item) {
|
||||||
|
for(var key in item) {
|
||||||
|
var value = item[key]
|
||||||
|
switch(key) {
|
||||||
|
case 'operation':
|
||||||
|
case 'status':
|
||||||
|
item[key] = $filter('tr')(value);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
vm.refreshPending = false;
|
vm.refreshPending = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,6 +225,19 @@
|
|||||||
ctrl.retrieveJob($(this).attr('policy_id'));
|
ctrl.retrieveJob($(this).attr('policy_id'));
|
||||||
ctrl.lastPolicyId = $(this).attr('policy_id');
|
ctrl.lastPolicyId = $(this).attr('policy_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
element.find('.datetimepicker').datetimepicker({
|
||||||
|
locale: 'en-US',
|
||||||
|
ignoreReadonly: true,
|
||||||
|
format: 'L',
|
||||||
|
showClear: true
|
||||||
|
});
|
||||||
|
element.find('#fromDatePicker').on('blur', function(){
|
||||||
|
ctrl.pickUp({'key': 'fromDate', 'value': $(this).val()});
|
||||||
|
});
|
||||||
|
element.find('#toDatePicker').on('blur', function(){
|
||||||
|
ctrl.pickUp({'key': 'toDate', 'value': $(this).val()});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<div class="tab-pane active" id="repositories" element-height>
|
<div class="tab-pane" id="repositories" element-height>
|
||||||
<div class="col-xs-12 col-md-12 each-tab-pane">
|
<div class="col-xs-12 col-md-12 each-tab-pane">
|
||||||
<div class="form-inline">
|
<div class="form-inline">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
@ -8,10 +8,11 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ng-if="vm.repositories.length === 0" class="empty-hint">
|
<div class="pane">
|
||||||
<h3 style="margin-top: 200px;" class="text-muted">// 'no_repositories' | tr //</h3>
|
|
||||||
</div>
|
|
||||||
<div class="sub-pane">
|
<div class="sub-pane">
|
||||||
|
<div ng-if="vm.repositories.length === 0" class="empty-hint">
|
||||||
|
<h3 style="margin-top: 120px;" class="text-muted">// 'no_repositories' | tr //</h3>
|
||||||
|
</div>
|
||||||
<div ng-if="vm.repositories.length > 0" class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
|
<div ng-if="vm.repositories.length > 0" class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
|
||||||
<modal-dialog action="vm.deleteImage()" content-type="text/html" modal-title="//vm.modalTitle//" modal-message="//vm.modalMessage//"></modal-dialog>
|
<modal-dialog action="vm.deleteImage()" content-type="text/html" modal-title="//vm.modalTitle//" modal-message="//vm.modalMessage//"></modal-dialog>
|
||||||
<div class="panel panel-default" ng-repeat="repo in vm.repositories" ng-show="vm.tagCount[repo] > 0">
|
<div class="panel panel-default" ng-repeat="repo in vm.repositories" ng-show="vm.tagCount[repo] > 0">
|
||||||
@ -29,3 +30,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
@ -1,4 +1,7 @@
|
|||||||
<h4 class="page-header title-color underlined">// 'summary' | tr //</h4>
|
<h4 class="page-header title-color underlined">// 'summary' | tr //</h4>
|
||||||
<dl class="page-content dl-horizontal" ng-repeat="(key, value) in vm.statProjects">
|
<dl class="page-content dl-horizontal" ng-repeat="(key, value) in vm.statProjects">
|
||||||
<dt>// key | tr //:</dt><dd>//value//<dd>
|
<dt ng-if="$index==0"><a href="/project">// key | tr //</a>:</dt><dd ng-if="$index==0">//value//<dd>
|
||||||
|
<dt ng-if="$index==1">// key | tr //:</dt><dd ng-if="$index==1">//value//<dd>
|
||||||
|
<dt ng-if="$index==2"><a href="/search?q=">// key | tr //</a>:</dt><dd ng-if="$index==2">//value//<dd>
|
||||||
|
<dt ng-if="$index>2">// key | tr //:</dt><dd ng-if="$index>2">//value//<dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="submit" class="btn btn-primary" id="btnOk" ng-click="form.$valid && vm.save(destination)">// 'ok' | tr //</button>
|
<button ng-show="vm.editable" type="submit" class="btn btn-primary" id="btnOk" ng-click="vm.save(destination)">// 'ok' | tr //</button>
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">// 'close' | tr //</button>
|
<button type="button" class="btn btn-default" data-dismiss="modal">// 'close' | tr //</button>
|
||||||
</div>
|
</div>
|
||||||
</div><!-- /.modal-content -->
|
</div><!-- /.modal-content -->
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
<table class="table table-pane">
|
<table class="table table-pane">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr ng-if="vm.replications.length == 0">
|
<tr ng-if="vm.replications.length == 0">
|
||||||
<td colspan="7" height="100%" class="empty-hint" ><h3 class="text-muted">// 'no_replications' | tr //</h3></td>
|
<td colspan="7" height="100%" class="empty-hint" ><h3 class="text-muted">// 'no_replication_policies' | tr //</h3></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr ng-if="vm.replications.length > 0" ng-repeat="r in vm.replications">
|
<tr ng-if="vm.replications.length > 0" ng-repeat="r in vm.replications">
|
||||||
<td><a href="repository#/replication?project_id=//r.project_id//">//r.name//</a></td>
|
<td><a href="repository#/replication?project_id=//r.project_id//">//r.name//</a></td>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<h4 class="page-header title-color underlined">// 'popular_repositories' | tr //</h4>
|
<h4 class="page-header title-color underlined">// 'popular_repositories' | tr //</h4>
|
||||||
<div class="col-xs-4 col-md-12 up-table-pane">
|
<div class="col-xs-4 col-md-12 up-table-pane">
|
||||||
<div class="table-head-container" style="width: 650px">
|
<div class="table-head-container">
|
||||||
<table class="table table-pane table-header">
|
<table class="table table-pane table-header">
|
||||||
<thead>
|
<thead>
|
||||||
<th width="60%">// 'repository_name' | tr //</th>
|
<th width="60%">// 'repository_name' | tr //</th>
|
||||||
@ -9,7 +9,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-body-container" style="height: 120px; width: 650px">
|
<div class="table-body-container" ng-style="vm.customBodyHeight">
|
||||||
<table class="table table-pane">
|
<table class="table table-pane">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -30,7 +30,9 @@
|
|||||||
'restrict': 'E',
|
'restrict': 'E',
|
||||||
'templateUrl': '/static/resources/js/components/top-repository/top-repository.directive.html',
|
'templateUrl': '/static/resources/js/components/top-repository/top-repository.directive.html',
|
||||||
'controller': TopRepositoryController,
|
'controller': TopRepositoryController,
|
||||||
'scope' : true,
|
'scope' : {
|
||||||
|
'customBodyHeight': '='
|
||||||
|
},
|
||||||
'controllerAs': 'vm',
|
'controllerAs': 'vm',
|
||||||
'bindToController': true
|
'bindToController': true
|
||||||
};
|
};
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="table-body-container">
|
<div class="table-body-container" style="height: 220px;">
|
||||||
<table class="table table-pane">
|
<table class="table table-pane">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
DashboardController.$inject = ['$scope'];
|
DashboardController.$inject = ['$scope'];
|
||||||
|
|
||||||
function DashboardController($scope) {
|
function DashboardController($scope) {
|
||||||
|
var vm = this;
|
||||||
|
vm.customBodyHeight = {'height': '165px'};
|
||||||
}
|
}
|
||||||
|
|
||||||
})();
|
})();
|
@ -14,8 +14,11 @@
|
|||||||
$scope.subsSection = 32;
|
$scope.subsSection = 32;
|
||||||
$scope.subsSubPane = 226;
|
$scope.subsSubPane = 226;
|
||||||
|
|
||||||
|
|
||||||
var vm = this;
|
var vm = this;
|
||||||
|
|
||||||
|
vm.customBodyHeight = {'height': '180px'};
|
||||||
|
|
||||||
var indexDesc = $filter('tr')('index_desc', []);
|
var indexDesc = $filter('tr')('index_desc', []);
|
||||||
var indexDesc1 = $filter('tr')('index_desc_1', []);
|
var indexDesc1 = $filter('tr')('index_desc_1', []);
|
||||||
var indexDesc2 = $filter('tr')('index_desc_2', []);
|
var indexDesc2 = $filter('tr')('index_desc_2', []);
|
||||||
|
@ -154,8 +154,8 @@ var locale_messages = {
|
|||||||
'logs' : 'Logs',
|
'logs' : 'Logs',
|
||||||
'enabled': 'Enabled',
|
'enabled': 'Enabled',
|
||||||
'disabled': 'Disabled',
|
'disabled': 'Disabled',
|
||||||
'no_replication_policies': 'No replication policies, add new replication policy.',
|
'no_replication_policies_add_new': 'No replication policies, add new replication policy.',
|
||||||
'no_replications': 'No replications.',
|
'no_replication_policies': 'No replication policies.',
|
||||||
'no_replication_jobs': 'No replication jobs.',
|
'no_replication_jobs': 'No replication jobs.',
|
||||||
'no_destinations': 'No destinations, add new destination.',
|
'no_destinations': 'No destinations, add new destination.',
|
||||||
'name_is_required': 'Name is required.',
|
'name_is_required': 'Name is required.',
|
||||||
@ -200,5 +200,13 @@ var locale_messages = {
|
|||||||
'delete_repo': 'Delete Repo',
|
'delete_repo': 'Delete Repo',
|
||||||
'download_log': 'Download Logs',
|
'download_log': 'Download Logs',
|
||||||
'edit': 'Edit',
|
'edit': 'Edit',
|
||||||
'delete': 'Delete'
|
'delete': 'Delete',
|
||||||
|
'transfer': 'Transfer',
|
||||||
|
'all': 'All',
|
||||||
|
'pending': 'Pending',
|
||||||
|
'running': 'Running',
|
||||||
|
'finished': 'Finished',
|
||||||
|
'canceled': 'Canceled',
|
||||||
|
'stopped': 'Stopped',
|
||||||
|
'error': 'Error'
|
||||||
};
|
};
|
@ -152,7 +152,8 @@ var locale_messages = {
|
|||||||
'logs': '日志',
|
'logs': '日志',
|
||||||
'enabled': '启用',
|
'enabled': '启用',
|
||||||
'disabled': '停用',
|
'disabled': '停用',
|
||||||
'no_replication_policies': '没有复制策略,请新增复制策略。',
|
'no_replication_policies_add_new': '没有复制策略,请新增复制策略。',
|
||||||
|
'no_replication_policies': '没有复制策略。',
|
||||||
'no_replications': '没有复制策略。',
|
'no_replications': '没有复制策略。',
|
||||||
'no_replication_jobs': '没有复制任务。',
|
'no_replication_jobs': '没有复制任务。',
|
||||||
'no_destinations': '没有目标设置,请新增目标。',
|
'no_destinations': '没有目标设置,请新增目标。',
|
||||||
@ -199,5 +200,13 @@ var locale_messages = {
|
|||||||
'delete_repo': '删除镜像仓库',
|
'delete_repo': '删除镜像仓库',
|
||||||
'download_log': '下载日志',
|
'download_log': '下载日志',
|
||||||
'edit': '修改',
|
'edit': '修改',
|
||||||
'delete': '删除'
|
'delete': '删除',
|
||||||
|
'all': '全部',
|
||||||
|
'transfer': '传输',
|
||||||
|
'pending': '挂起',
|
||||||
|
'running': '进行中',
|
||||||
|
'finished': '已完成',
|
||||||
|
'canceled': '取消',
|
||||||
|
'stopped': '停止',
|
||||||
|
'error': '错误'
|
||||||
};
|
};
|
@ -12,12 +12,13 @@
|
|||||||
|
|
||||||
return listReplicationJob;
|
return listReplicationJob;
|
||||||
|
|
||||||
function listReplicationJob(policyId, repository) {
|
function listReplicationJob(policyId, repository, status) {
|
||||||
return $http
|
return $http
|
||||||
.get('/api/jobs/replication/', {
|
.get('/api/jobs/replication/', {
|
||||||
'params': {
|
'params': {
|
||||||
'policy_id': policyId,
|
'policy_id': policyId,
|
||||||
'repository': repository
|
'repository': repository,
|
||||||
|
'status': status
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-xs-8 col-md-8">
|
<div class="col-xs-8 col-md-8">
|
||||||
<div class="up-section">
|
<div class="up-section">
|
||||||
<top-repository></top-repository>
|
<top-repository custom-body-height="vm.customBodyHeight"></top-repository>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -52,7 +52,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<div class="down-section">
|
<div class="down-section">
|
||||||
<top-repository></top-repository>
|
<top-repository custom-body-height="vm.customBodyHeight"></top-repository>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user