mirror of
https://github.com/goharbor/harbor
synced 2024-09-21 07:22:20 +00:00
Implement job log viewer
This commit is contained in:
parent
a9a79736b4
commit
9057f751e3
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "harbor-ui",
|
"name": "harbor-ui",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"description": "Harbor shared UI components based on Clarity and Angular4",
|
"description": "Harbor shared UI components based on Clarity and Angular4",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "ng serve --host 0.0.0.0 --port 4500 --proxy-config proxy.config.json",
|
"start": "ng serve --host 0.0.0.0 --port 4500 --proxy-config proxy.config.json",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "harbor-ui",
|
"name": "harbor-ui",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"description": "Harbor shared UI components based on Clarity and Angular4",
|
"description": "Harbor shared UI components based on Clarity and Angular4",
|
||||||
"author": "VMware",
|
"author": "VMware",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
|
@ -43,4 +43,4 @@
|
||||||
"web-animations-js": "^2.2.1",
|
"web-animations-js": "^2.2.1",
|
||||||
"zone.js": "^0.8.4"
|
"zone.js": "^0.8.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,13 @@ export const VULNERABILITY_CONFIG_HTML: string = `
|
||||||
<label class="section-title" *ngIf="showSubTitle">{{ 'CONFIG.SCANNING.TITLE' | translate }}</label>
|
<label class="section-title" *ngIf="showSubTitle">{{ 'CONFIG.SCANNING.TITLE' | translate }}</label>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>{{ 'CONFIG.SCANNING.DB_REFRESH_TIME' | translate }}</label>
|
<label>{{ 'CONFIG.SCANNING.DB_REFRESH_TIME' | translate }}</label>
|
||||||
<clr-dropdown [clrMenuPosition]="'bottom-right'" style="margin-top:-8px;" class="clr-dropdown-override">
|
<clr-tooltip *ngIf="!isClairDBFullyReady" [clrTooltipDirection]="'top-right'" [clrTooltipSize]="'md'">
|
||||||
|
<clr-icon shape="warning" class="is-warning" size="22"></clr-icon>
|
||||||
|
<clr-tooltip-content>
|
||||||
|
<span>{{'CONFIG.SCANNING.DB_NOT_READY' | translate }}</span>
|
||||||
|
</clr-tooltip-content>
|
||||||
|
</clr-tooltip>
|
||||||
|
<clr-dropdown *ngIf="isClairDBFullyReady" [clrMenuPosition]="'bottom-right'" style="margin-top:-8px;" class="clr-dropdown-override">
|
||||||
<button class="btn btn-link btn-font" clrDropdownToggle>
|
<button class="btn btn-link btn-font" clrDropdownToggle>
|
||||||
{{ updatedTimestamp }}
|
{{ updatedTimestamp }}
|
||||||
<clr-icon shape="caret down"></clr-icon>
|
<clr-icon shape="caret down"></clr-icon>
|
||||||
|
@ -23,7 +29,6 @@ export const VULNERABILITY_CONFIG_HTML: string = `
|
||||||
<select id="scanAllPolicy" name="scanAllPolicy" [disabled]="!editable" [(ngModel)]="scanningType">
|
<select id="scanAllPolicy" name="scanAllPolicy" [disabled]="!editable" [(ngModel)]="scanningType">
|
||||||
<option value="none">{{ 'CONFIG.SCANNING.NONE_POLICY' | translate }}</option>
|
<option value="none">{{ 'CONFIG.SCANNING.NONE_POLICY' | translate }}</option>
|
||||||
<option value="daily">{{ 'CONFIG.SCANNING.DAILY_POLICY' | translate }}</option>
|
<option value="daily">{{ 'CONFIG.SCANNING.DAILY_POLICY' | translate }}</option>
|
||||||
<option value="on_refresh">{{ 'CONFIG.SCANNING.REFRESH_POLICY' | translate }}</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<input type="time" name="dailyTimePicker" [disabled]="!editable" [hidden]="!showTimePicker" [(ngModel)]="dailyTime" />
|
<input type="time" name="dailyTimePicker" [disabled]="!editable" [hidden]="!showTimePicker" [(ngModel)]="dailyTime" />
|
||||||
|
|
|
@ -206,6 +206,10 @@ export class VulnerabilityConfigComponent {
|
||||||
this.vulnerabilityConfig.scan_all_policy.value.type === "daily";
|
this.vulnerabilityConfig.scan_all_policy.value.type === "daily";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isClairDBFullyReady(): boolean {
|
||||||
|
return this.clairDBStatus && this.clairDBStatus.overall_last_update > 0;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private scanningService: ScanningResultService,
|
private scanningService: ScanningResultService,
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandler: ErrorHandler,
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { ErrorHandler } from '../error-handler/error-handler';
|
||||||
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||||
import { ReplicationService, ReplicationDefaultService } from '../service/replication.service';
|
import { ReplicationService, ReplicationDefaultService } from '../service/replication.service';
|
||||||
import { EndpointService, EndpointDefaultService } from '../service/endpoint.service';
|
import { EndpointService, EndpointDefaultService } from '../service/endpoint.service';
|
||||||
|
import { JobLogViewerComponent } from '../job-log-viewer/job-log-viewer.component';
|
||||||
|
|
||||||
describe('CreateEditRuleComponent (inline template)', ()=>{
|
describe('CreateEditRuleComponent (inline template)', ()=>{
|
||||||
|
|
||||||
|
@ -175,7 +176,8 @@ describe('CreateEditRuleComponent (inline template)', ()=>{
|
||||||
ConfirmationDialogComponent,
|
ConfirmationDialogComponent,
|
||||||
DatePickerComponent,
|
DatePickerComponent,
|
||||||
FilterComponent,
|
FilterComponent,
|
||||||
InlineAlertComponent
|
InlineAlertComponent,
|
||||||
|
JobLogViewerComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
ErrorHandler,
|
ErrorHandler,
|
||||||
|
|
|
@ -23,6 +23,7 @@ import { DATETIME_PICKER_DIRECTIVES } from './datetime-picker/index';
|
||||||
import { VULNERABILITY_DIRECTIVES } from './vulnerability-scanning/index';
|
import { VULNERABILITY_DIRECTIVES } from './vulnerability-scanning/index';
|
||||||
import { PUSH_IMAGE_BUTTON_DIRECTIVES } from './push-image/index';
|
import { PUSH_IMAGE_BUTTON_DIRECTIVES } from './push-image/index';
|
||||||
import { CONFIGURATION_DIRECTIVES } from './config/index';
|
import { CONFIGURATION_DIRECTIVES } from './config/index';
|
||||||
|
import { JOB_LOG_VIEWER_DIRECTIVES } from './job-log-viewer/index';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SystemInfoService,
|
SystemInfoService,
|
||||||
|
@ -153,7 +154,8 @@ export function initConfig(translateInitializer: TranslateServiceInitializer, co
|
||||||
DATETIME_PICKER_DIRECTIVES,
|
DATETIME_PICKER_DIRECTIVES,
|
||||||
VULNERABILITY_DIRECTIVES,
|
VULNERABILITY_DIRECTIVES,
|
||||||
PUSH_IMAGE_BUTTON_DIRECTIVES,
|
PUSH_IMAGE_BUTTON_DIRECTIVES,
|
||||||
CONFIGURATION_DIRECTIVES
|
CONFIGURATION_DIRECTIVES,
|
||||||
|
JOB_LOG_VIEWER_DIRECTIVES
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
LOG_DIRECTIVES,
|
LOG_DIRECTIVES,
|
||||||
|
@ -173,6 +175,7 @@ export function initConfig(translateInitializer: TranslateServiceInitializer, co
|
||||||
VULNERABILITY_DIRECTIVES,
|
VULNERABILITY_DIRECTIVES,
|
||||||
PUSH_IMAGE_BUTTON_DIRECTIVES,
|
PUSH_IMAGE_BUTTON_DIRECTIVES,
|
||||||
CONFIGURATION_DIRECTIVES,
|
CONFIGURATION_DIRECTIVES,
|
||||||
|
JOB_LOG_VIEWER_DIRECTIVES,
|
||||||
TranslateModule
|
TranslateModule
|
||||||
],
|
],
|
||||||
providers: []
|
providers: []
|
||||||
|
|
|
@ -15,4 +15,5 @@ export * from './vulnerability-scanning/index';
|
||||||
export * from './i18n/index';
|
export * from './i18n/index';
|
||||||
export * from './push-image/index';
|
export * from './push-image/index';
|
||||||
export * from './third-party/index';
|
export * from './third-party/index';
|
||||||
export * from './config/index';
|
export * from './config/index';
|
||||||
|
export * from './job-log-viewer/index';
|
9
src/ui_ng/lib/src/job-log-viewer/index.ts
Normal file
9
src/ui_ng/lib/src/job-log-viewer/index.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { Type } from '@angular/core';
|
||||||
|
|
||||||
|
import { JobLogViewerComponent } from './job-log-viewer.component';
|
||||||
|
|
||||||
|
export * from './job-log-viewer.component';
|
||||||
|
|
||||||
|
export const JOB_LOG_VIEWER_DIRECTIVES: Type<any>[] = [
|
||||||
|
JobLogViewerComponent
|
||||||
|
];
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
import { ReplicationService, ReplicationDefaultService } from '../service/index';
|
||||||
|
|
||||||
|
import { JobLogViewerComponent } from './job-log-viewer.component';
|
||||||
|
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||||
|
import { ErrorHandler } from '../error-handler/index';
|
||||||
|
import { SharedModule } from '../shared/shared.module';
|
||||||
|
|
||||||
|
describe('JobLogViewerComponent (inline template)', () => {
|
||||||
|
let component: JobLogViewerComponent;
|
||||||
|
let fixture: ComponentFixture<JobLogViewerComponent>;
|
||||||
|
let serviceConfig: IServiceConfig;
|
||||||
|
let replicationService: ReplicationService;
|
||||||
|
let spy: jasmine.Spy;
|
||||||
|
let testConfig: IServiceConfig = {
|
||||||
|
replicationJobEndpoint: "/api/jobs/replication/testing"
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
SharedModule,
|
||||||
|
BrowserAnimationsModule
|
||||||
|
],
|
||||||
|
declarations: [JobLogViewerComponent],
|
||||||
|
providers: [
|
||||||
|
ErrorHandler,
|
||||||
|
{ provide: SERVICE_CONFIG, useValue: testConfig },
|
||||||
|
{ provide: ReplicationService, useClass: ReplicationDefaultService }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(JobLogViewerComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
|
||||||
|
serviceConfig = TestBed.get(SERVICE_CONFIG);
|
||||||
|
replicationService = fixture.debugElement.injector.get(ReplicationService);
|
||||||
|
spy = spyOn(replicationService, 'getJobLog')
|
||||||
|
.and.returnValues(Promise.resolve("job log text"));
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
expect(serviceConfig).toBeTruthy();
|
||||||
|
expect(serviceConfig.replicationJobEndpoint).toEqual("/api/jobs/replication/testing");
|
||||||
|
|
||||||
|
component.open(16);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,33 @@
|
||||||
|
export const JOB_LOG_VIEWER_TEMPLATE: string = `
|
||||||
|
<clr-modal [(clrModalOpen)]="opened" [clrModalStaticBackdrop]="true" [clrModalSize]="'xl'">
|
||||||
|
<h3 class="modal-title" class="log-viewer-title" style="margin-top: 0px;">{{'REPLICATION.JOB_LOG_VIEWER' | translate }}</h3>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="loading-back" [hidden]="!onGoing">
|
||||||
|
<span class="spinner spinner-md"></span>
|
||||||
|
</div>
|
||||||
|
<pre [hidden]="onGoing">
|
||||||
|
<code>
|
||||||
|
{{log}}
|
||||||
|
</code>
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-primary" (click)="close()">{{ 'BUTTON.CLOSE' | translate}}</button>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const JOB_LOG_VIEWER_STYLES: string = `
|
||||||
|
.log-viewer-title {
|
||||||
|
line-height: 24px;
|
||||||
|
color: #000000;
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-back {
|
||||||
|
height: 358px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
`;
|
60
src/ui_ng/lib/src/job-log-viewer/job-log-viewer.component.ts
Normal file
60
src/ui_ng/lib/src/job-log-viewer/job-log-viewer.component.ts
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
import { JOB_LOG_VIEWER_TEMPLATE, JOB_LOG_VIEWER_STYLES } from './job-log-viewer.component.template';
|
||||||
|
import { ReplicationService } from '../service/index';
|
||||||
|
import { ErrorHandler } from '../error-handler/index';
|
||||||
|
import { toPromise } from '../utils';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'job-log-viewer',
|
||||||
|
template: JOB_LOG_VIEWER_TEMPLATE,
|
||||||
|
styles: [JOB_LOG_VIEWER_STYLES]
|
||||||
|
})
|
||||||
|
|
||||||
|
export class JobLogViewerComponent {
|
||||||
|
opened: boolean = false;
|
||||||
|
log: string = '';
|
||||||
|
onGoing: boolean = true;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private replicationService: ReplicationService,
|
||||||
|
private errorHandler: ErrorHandler
|
||||||
|
) { }
|
||||||
|
|
||||||
|
open(jobId: number | string): void {
|
||||||
|
this.opened = true;
|
||||||
|
this.load(jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(): void {
|
||||||
|
this.opened = false;
|
||||||
|
this.log = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
load(jobId: number | string): void {
|
||||||
|
this.onGoing = true;
|
||||||
|
|
||||||
|
toPromise<string>(this.replicationService.getJobLog(jobId))
|
||||||
|
.then((log: string) => {
|
||||||
|
this.onGoing = false;
|
||||||
|
this.log = log;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.onGoing = false;
|
||||||
|
this.errorHandler.error(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,7 +61,7 @@ export const REPLICATION_TEMPLATE: string = `
|
||||||
<clr-dg-cell>{{j.creation_time | date: 'short'}}</clr-dg-cell>
|
<clr-dg-cell>{{j.creation_time | date: 'short'}}</clr-dg-cell>
|
||||||
<clr-dg-cell>{{j.update_time | date: 'short'}}</clr-dg-cell>
|
<clr-dg-cell>{{j.update_time | date: 'short'}}</clr-dg-cell>
|
||||||
<clr-dg-cell>
|
<clr-dg-cell>
|
||||||
<a href="/api/jobs/replication/{{j.id}}/log" target="_BLANK">
|
<a href="javascript:void(0);" (click)="viewLog(j.id)">
|
||||||
<clr-icon shape="clipboard"></clr-icon>
|
<clr-icon shape="clipboard"></clr-icon>
|
||||||
</a>
|
</a>
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
|
@ -73,4 +73,5 @@ export const REPLICATION_TEMPLATE: string = `
|
||||||
</clr-dg-footer>
|
</clr-dg-footer>
|
||||||
</clr-datagrid>
|
</clr-datagrid>
|
||||||
</div>
|
</div>
|
||||||
|
<job-log-viewer #replicationLogViewer></job-log-viewer>
|
||||||
</div>`;
|
</div>`;
|
|
@ -18,6 +18,7 @@ import { ErrorHandler } from '../error-handler/error-handler';
|
||||||
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||||
import { ReplicationService, ReplicationDefaultService } from '../service/replication.service';
|
import { ReplicationService, ReplicationDefaultService } from '../service/replication.service';
|
||||||
import { EndpointService, EndpointDefaultService } from '../service/endpoint.service';
|
import { EndpointService, EndpointDefaultService } from '../service/endpoint.service';
|
||||||
|
import { JobLogViewerComponent } from '../job-log-viewer/job-log-viewer.component';
|
||||||
|
|
||||||
describe('Replication Component (inline template)', ()=>{
|
describe('Replication Component (inline template)', ()=>{
|
||||||
|
|
||||||
|
@ -175,7 +176,8 @@ describe('Replication Component (inline template)', ()=>{
|
||||||
ConfirmationDialogComponent,
|
ConfirmationDialogComponent,
|
||||||
DatePickerComponent,
|
DatePickerComponent,
|
||||||
FilterComponent,
|
FilterComponent,
|
||||||
InlineAlertComponent
|
InlineAlertComponent,
|
||||||
|
JobLogViewerComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
ErrorHandler,
|
ErrorHandler,
|
||||||
|
|
|
@ -17,7 +17,7 @@ import { NgModel } from '@angular/forms';
|
||||||
|
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
import { ListReplicationRuleComponent} from '../list-replication-rule/list-replication-rule.component';
|
import { ListReplicationRuleComponent } from '../list-replication-rule/list-replication-rule.component';
|
||||||
import { CreateEditRuleComponent } from '../create-edit-rule/create-edit-rule.component';
|
import { CreateEditRuleComponent } from '../create-edit-rule/create-edit-rule.component';
|
||||||
import { ErrorHandler } from '../error-handler/error-handler';
|
import { ErrorHandler } from '../error-handler/error-handler';
|
||||||
|
|
||||||
|
@ -32,24 +32,26 @@ import { Comparator } from 'clarity-angular';
|
||||||
import { REPLICATION_TEMPLATE } from './replication.component.html';
|
import { REPLICATION_TEMPLATE } from './replication.component.html';
|
||||||
import { REPLICATION_STYLE } from './replication.component.css';
|
import { REPLICATION_STYLE } from './replication.component.css';
|
||||||
|
|
||||||
const ruleStatus: {[key: string]: any} = [
|
import { JobLogViewerComponent } from '../job-log-viewer/index';
|
||||||
{ 'key': 'all', 'description': 'REPLICATION.ALL_STATUS'},
|
|
||||||
{ 'key': '1', 'description': 'REPLICATION.ENABLED'},
|
const ruleStatus: { [key: string]: any } = [
|
||||||
{ 'key': '0', 'description': 'REPLICATION.DISABLED'}
|
{ 'key': 'all', 'description': 'REPLICATION.ALL_STATUS' },
|
||||||
|
{ 'key': '1', 'description': 'REPLICATION.ENABLED' },
|
||||||
|
{ 'key': '0', 'description': 'REPLICATION.DISABLED' }
|
||||||
];
|
];
|
||||||
|
|
||||||
const jobStatus: {[key: string]: any} = [
|
const jobStatus: { [key: string]: any } = [
|
||||||
{ 'key': 'all', 'description': 'REPLICATION.ALL' },
|
{ 'key': 'all', 'description': 'REPLICATION.ALL' },
|
||||||
{ 'key': 'pending', 'description': 'REPLICATION.PENDING' },
|
{ 'key': 'pending', 'description': 'REPLICATION.PENDING' },
|
||||||
{ 'key': 'running', 'description': 'REPLICATION.RUNNING' },
|
{ 'key': 'running', 'description': 'REPLICATION.RUNNING' },
|
||||||
{ 'key': 'error', 'description': 'REPLICATION.ERROR' },
|
{ 'key': 'error', 'description': 'REPLICATION.ERROR' },
|
||||||
{ 'key': 'retrying', 'description': 'REPLICATION.RETRYING' },
|
{ 'key': 'retrying', 'description': 'REPLICATION.RETRYING' },
|
||||||
{ 'key': 'stopped' , 'description': 'REPLICATION.STOPPED' },
|
{ 'key': 'stopped', 'description': 'REPLICATION.STOPPED' },
|
||||||
{ 'key': 'finished', 'description': 'REPLICATION.FINISHED' },
|
{ 'key': 'finished', 'description': 'REPLICATION.FINISHED' },
|
||||||
{ 'key': 'canceled', 'description': 'REPLICATION.CANCELED' }
|
{ 'key': 'canceled', 'description': 'REPLICATION.CANCELED' }
|
||||||
];
|
];
|
||||||
|
|
||||||
const optionalSearch: {} = {0: 'REPLICATION.ADVANCED', 1: 'REPLICATION.SIMPLE'};
|
const optionalSearch: {} = { 0: 'REPLICATION.ADVANCED', 1: 'REPLICATION.SIMPLE' };
|
||||||
|
|
||||||
export class SearchOption {
|
export class SearchOption {
|
||||||
ruleId: number | string;
|
ruleId: number | string;
|
||||||
|
@ -67,163 +69,172 @@ export class SearchOption {
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'hbr-replication',
|
selector: 'hbr-replication',
|
||||||
template: REPLICATION_TEMPLATE,
|
template: REPLICATION_TEMPLATE,
|
||||||
styles: [ REPLICATION_STYLE ]
|
styles: [REPLICATION_STYLE]
|
||||||
})
|
})
|
||||||
export class ReplicationComponent implements OnInit {
|
export class ReplicationComponent implements OnInit {
|
||||||
|
|
||||||
@Input() projectId: number | string;
|
|
||||||
@Input() withReplicationJob: boolean;
|
|
||||||
|
|
||||||
@Output() redirect = new EventEmitter<ReplicationRule>();
|
@Input() projectId: number | string;
|
||||||
|
@Input() withReplicationJob: boolean;
|
||||||
|
|
||||||
search: SearchOption = new SearchOption();
|
@Output() redirect = new EventEmitter<ReplicationRule>();
|
||||||
|
|
||||||
ruleStatus = ruleStatus;
|
search: SearchOption = new SearchOption();
|
||||||
currentRuleStatus: {key: string, description: string};
|
|
||||||
|
|
||||||
jobStatus = jobStatus;
|
ruleStatus = ruleStatus;
|
||||||
currentJobStatus: {key: string, description: string};
|
currentRuleStatus: { key: string, description: string };
|
||||||
|
|
||||||
changedRules: ReplicationRule[];
|
jobStatus = jobStatus;
|
||||||
initSelectedId: number | string;
|
currentJobStatus: { key: string, description: string };
|
||||||
|
|
||||||
rules: ReplicationRule[];
|
changedRules: ReplicationRule[];
|
||||||
loading: boolean;
|
initSelectedId: number | string;
|
||||||
|
|
||||||
jobs: ReplicationJob[];
|
rules: ReplicationRule[];
|
||||||
|
loading: boolean;
|
||||||
|
|
||||||
jobsTotalRecordCount: number;
|
jobs: ReplicationJob[];
|
||||||
jobsTotalPage: number;
|
|
||||||
|
|
||||||
toggleJobSearchOption = optionalSearch;
|
jobsTotalRecordCount: number;
|
||||||
currentJobSearchOption: number;
|
jobsTotalPage: number;
|
||||||
|
|
||||||
@ViewChild(ListReplicationRuleComponent)
|
toggleJobSearchOption = optionalSearch;
|
||||||
listReplicationRule: ListReplicationRuleComponent;
|
currentJobSearchOption: number;
|
||||||
|
|
||||||
@ViewChild(CreateEditRuleComponent)
|
@ViewChild(ListReplicationRuleComponent)
|
||||||
createEditPolicyComponent: CreateEditRuleComponent;
|
listReplicationRule: ListReplicationRuleComponent;
|
||||||
|
|
||||||
creationTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('creation_time', 'date');
|
@ViewChild(CreateEditRuleComponent)
|
||||||
updateTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('update_time', 'date');
|
createEditPolicyComponent: CreateEditRuleComponent;
|
||||||
|
|
||||||
constructor(
|
@ViewChild("replicationLogViewer")
|
||||||
private errorHandler: ErrorHandler,
|
replicationLogViewer: JobLogViewerComponent;
|
||||||
private replicationService: ReplicationService,
|
|
||||||
private translateService: TranslateService) {
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
creationTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('creation_time', 'date');
|
||||||
this.currentRuleStatus = this.ruleStatus[0];
|
updateTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('update_time', 'date');
|
||||||
this.currentJobStatus = this.jobStatus[0];
|
|
||||||
this.currentJobSearchOption = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
openModal(): void {
|
constructor(
|
||||||
this.createEditPolicyComponent.openCreateEditRule(true);
|
private errorHandler: ErrorHandler,
|
||||||
}
|
private replicationService: ReplicationService,
|
||||||
|
private translateService: TranslateService) {
|
||||||
|
}
|
||||||
|
|
||||||
openEditRule(rule: ReplicationRule) {
|
ngOnInit() {
|
||||||
if(rule) {
|
this.currentRuleStatus = this.ruleStatus[0];
|
||||||
let editable = true;
|
this.currentJobStatus = this.jobStatus[0];
|
||||||
if(rule.enabled === 1) {
|
this.currentJobSearchOption = 0;
|
||||||
editable = false;
|
}
|
||||||
}
|
|
||||||
this.createEditPolicyComponent.openCreateEditRule(editable, rule.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchReplicationJobs() {
|
openModal(): void {
|
||||||
|
this.createEditPolicyComponent.openCreateEditRule(true);
|
||||||
let params: RequestQueryParams = new RequestQueryParams();
|
}
|
||||||
params.set('status', this.search.status);
|
|
||||||
params.set('repository', this.search.repoName);
|
|
||||||
params.set('start_time', this.search.startTimestamp);
|
|
||||||
params.set('end_time', this.search.endTimestamp);
|
|
||||||
|
|
||||||
toPromise<ReplicationJob[]>(this.replicationService
|
|
||||||
.getJobs(this.search.ruleId, params))
|
|
||||||
.then(
|
|
||||||
response=>{
|
|
||||||
this.jobs = response;
|
|
||||||
}).catch(error=>{
|
|
||||||
this.errorHandler.error(error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectOneRule(rule: ReplicationRule) {
|
openEditRule(rule: ReplicationRule) {
|
||||||
if (rule) {
|
if (rule) {
|
||||||
this.search.ruleId = rule.id || '';
|
let editable = true;
|
||||||
this.search.repoName = '';
|
if (rule.enabled === 1) {
|
||||||
this.search.status = '';
|
editable = false;
|
||||||
this.currentJobSearchOption = 0;
|
}
|
||||||
this.currentJobStatus = { 'key': 'all', 'description': 'REPLICATION.ALL' };
|
this.createEditPolicyComponent.openCreateEditRule(editable, rule.id);
|
||||||
this.fetchReplicationJobs();
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
customRedirect(rule: ReplicationRule) {
|
fetchReplicationJobs() {
|
||||||
this.redirect.emit(rule);
|
|
||||||
}
|
|
||||||
|
|
||||||
doSearchRules(ruleName: string) {
|
|
||||||
this.search.ruleName = ruleName;
|
|
||||||
this.listReplicationRule.retrieveRules(ruleName);
|
|
||||||
}
|
|
||||||
|
|
||||||
doFilterRuleStatus($event: any) {
|
let params: RequestQueryParams = new RequestQueryParams();
|
||||||
if ($event && $event.target && $event.target["value"]) {
|
params.set('status', this.search.status);
|
||||||
let status = $event.target["value"];
|
params.set('repository', this.search.repoName);
|
||||||
this.currentRuleStatus = this.ruleStatus.find((r: any)=>r.key === status);
|
params.set('start_time', this.search.startTimestamp);
|
||||||
this.listReplicationRule.filterRuleStatus(this.currentRuleStatus.key);
|
params.set('end_time', this.search.endTimestamp);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
doFilterJobStatus($event: any) {
|
toPromise<ReplicationJob[]>(this.replicationService
|
||||||
if ($event && $event.target && $event.target["value"]) {
|
.getJobs(this.search.ruleId, params))
|
||||||
let status = $event.target["value"];
|
.then(
|
||||||
|
response => {
|
||||||
this.currentJobStatus = this.jobStatus.find((r: any)=>r.key === status);
|
this.jobs = response;
|
||||||
if(this.currentJobStatus.key === 'all') {
|
}).catch(error => {
|
||||||
status = '';
|
this.errorHandler.error(error);
|
||||||
}
|
});
|
||||||
this.search.status = status;
|
}
|
||||||
this.doSearchJobs(this.search.repoName);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
doSearchJobs(repoName: string) {
|
selectOneRule(rule: ReplicationRule) {
|
||||||
this.search.repoName = repoName;
|
if (rule) {
|
||||||
this.fetchReplicationJobs();
|
this.search.ruleId = rule.id || '';
|
||||||
}
|
this.search.repoName = '';
|
||||||
|
this.search.status = '';
|
||||||
|
this.currentJobSearchOption = 0;
|
||||||
|
this.currentJobStatus = { 'key': 'all', 'description': 'REPLICATION.ALL' };
|
||||||
|
this.fetchReplicationJobs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
reloadRules(isReady: boolean) {
|
customRedirect(rule: ReplicationRule) {
|
||||||
if(isReady) {
|
this.redirect.emit(rule);
|
||||||
this.search.ruleName = '';
|
}
|
||||||
this.listReplicationRule.retrieveRules(this.search.ruleName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
refreshRules() {
|
doSearchRules(ruleName: string) {
|
||||||
this.listReplicationRule.retrieveRules();
|
this.search.ruleName = ruleName;
|
||||||
}
|
this.listReplicationRule.retrieveRules(ruleName);
|
||||||
|
}
|
||||||
|
|
||||||
refreshJobs() {
|
doFilterRuleStatus($event: any) {
|
||||||
this.fetchReplicationJobs();
|
if ($event && $event.target && $event.target["value"]) {
|
||||||
}
|
let status = $event.target["value"];
|
||||||
|
this.currentRuleStatus = this.ruleStatus.find((r: any) => r.key === status);
|
||||||
|
this.listReplicationRule.filterRuleStatus(this.currentRuleStatus.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
toggleSearchJobOptionalName(option: number) {
|
doFilterJobStatus($event: any) {
|
||||||
(option === 1) ? this.currentJobSearchOption = 0 : this.currentJobSearchOption = 1;
|
if ($event && $event.target && $event.target["value"]) {
|
||||||
}
|
let status = $event.target["value"];
|
||||||
|
|
||||||
doJobSearchByStartTime(fromTimestamp: string) {
|
this.currentJobStatus = this.jobStatus.find((r: any) => r.key === status);
|
||||||
this.search.startTimestamp = fromTimestamp;
|
if (this.currentJobStatus.key === 'all') {
|
||||||
this.fetchReplicationJobs();
|
status = '';
|
||||||
}
|
}
|
||||||
|
this.search.status = status;
|
||||||
|
this.doSearchJobs(this.search.repoName);
|
||||||
|
|
||||||
doJobSearchByEndTime(toTimestamp: string) {
|
}
|
||||||
this.search.endTimestamp = toTimestamp;
|
}
|
||||||
this.fetchReplicationJobs();
|
|
||||||
}
|
doSearchJobs(repoName: string) {
|
||||||
|
this.search.repoName = repoName;
|
||||||
|
this.fetchReplicationJobs();
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadRules(isReady: boolean) {
|
||||||
|
if (isReady) {
|
||||||
|
this.search.ruleName = '';
|
||||||
|
this.listReplicationRule.retrieveRules(this.search.ruleName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshRules() {
|
||||||
|
this.listReplicationRule.retrieveRules();
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshJobs() {
|
||||||
|
this.fetchReplicationJobs();
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleSearchJobOptionalName(option: number) {
|
||||||
|
(option === 1) ? this.currentJobSearchOption = 0 : this.currentJobSearchOption = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
doJobSearchByStartTime(fromTimestamp: string) {
|
||||||
|
this.search.startTimestamp = fromTimestamp;
|
||||||
|
this.fetchReplicationJobs();
|
||||||
|
}
|
||||||
|
|
||||||
|
doJobSearchByEndTime(toTimestamp: string) {
|
||||||
|
this.search.endTimestamp = toTimestamp;
|
||||||
|
this.fetchReplicationJobs();
|
||||||
|
}
|
||||||
|
|
||||||
|
viewLog(jobId: number | string): void {
|
||||||
|
if (this.replicationLogViewer) {
|
||||||
|
this.replicationLogViewer.open(jobId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -114,6 +114,16 @@ export abstract class ReplicationService {
|
||||||
* @memberOf ReplicationService
|
* @memberOf ReplicationService
|
||||||
*/
|
*/
|
||||||
abstract getJobs(ruleId: number | string, queryParams?: RequestQueryParams): Observable<ReplicationJob[]> | Promise<ReplicationJob[]> | ReplicationJob[];
|
abstract getJobs(ruleId: number | string, queryParams?: RequestQueryParams): Observable<ReplicationJob[]> | Promise<ReplicationJob[]> | ReplicationJob[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the log of the specified job.
|
||||||
|
*
|
||||||
|
* @abstract
|
||||||
|
* @param {(number | string)} jobId
|
||||||
|
* @returns {(Observable<string> | Promise<string> | string)}
|
||||||
|
* @memberof ReplicationService
|
||||||
|
*/
|
||||||
|
abstract getJobLog(jobId: number | string): Observable<string> | Promise<string> | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -242,4 +252,15 @@ export class ReplicationDefaultService extends ReplicationService {
|
||||||
.then(response => response.json() as ReplicationJob[])
|
.then(response => response.json() as ReplicationJob[])
|
||||||
.catch(error => Promise.reject(error));
|
.catch(error => Promise.reject(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getJobLog(jobId: number | string): Observable<string> | Promise<string> | string {
|
||||||
|
if (!jobId || jobId <= 0) {
|
||||||
|
return Promise.reject('Bad argument');
|
||||||
|
}
|
||||||
|
|
||||||
|
let logUrl: string = `${this._jobBaseUrl}/${jobId}/log`;
|
||||||
|
return this.http.get(logUrl).toPromise()
|
||||||
|
.then(response => response.text())
|
||||||
|
.catch(error => Promise.reject(error));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -16,7 +16,7 @@ export const TIP_COMPONENT_HTML: string = `
|
||||||
<span>{{highCount}} {{packageText(highCount) | translate }} {{'VULNERABILITY.SEVERITY.HIGH' | translate }}</span>
|
<span>{{highCount}} {{packageText(highCount) | translate }} {{'VULNERABILITY.SEVERITY.HIGH' | translate }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="hasMedium" class="bar-summary-item">
|
<div *ngIf="hasMedium" class="bar-summary-item">
|
||||||
<clr-icon *ngIf="hasMedium" shape="exclamation-triangle" class="is-warning" size="20"></clr-icon>
|
<clr-icon *ngIf="hasMedium" shape="exclamation-triangle" class="is-warning" size="22"></clr-icon>
|
||||||
<span>{{mediumCount}} {{packageText(mediumCount) | translate }} {{'VULNERABILITY.SEVERITY.MEDIUM' | translate }}</span>
|
<span>{{mediumCount}} {{packageText(mediumCount) | translate }} {{'VULNERABILITY.SEVERITY.MEDIUM' | translate }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="hasLow" class="bar-summary-item">
|
<div *ngIf="hasLow" class="bar-summary-item">
|
||||||
|
@ -24,7 +24,7 @@ export const TIP_COMPONENT_HTML: string = `
|
||||||
<span>{{lowCount}} {{packageText(lowCount) | translate }} {{'VULNERABILITY.SEVERITY.LOW' | translate }}</span>
|
<span>{{lowCount}} {{packageText(lowCount) | translate }} {{'VULNERABILITY.SEVERITY.LOW' | translate }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="hasUnknown" class="bar-summary-item">
|
<div *ngIf="hasUnknown" class="bar-summary-item">
|
||||||
<clr-icon shape="help" size="24"></clr-icon>
|
<clr-icon shape="help" size="20"></clr-icon>
|
||||||
<span>{{unknownCount}} {{packageText(unknownCount) | translate }} {{'VULNERABILITY.SEVERITY.UNKNOWN' | translate }}</span>
|
<span>{{unknownCount}} {{packageText(unknownCount) | translate }} {{'VULNERABILITY.SEVERITY.UNKNOWN' | translate }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="hasNone" class="bar-summary-item">
|
<div *ngIf="hasNone" class="bar-summary-item">
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
"clarity-icons": "^0.9.8",
|
"clarity-icons": "^0.9.8",
|
||||||
"clarity-ui": "^0.9.8",
|
"clarity-ui": "^0.9.8",
|
||||||
"core-js": "^2.4.1",
|
"core-js": "^2.4.1",
|
||||||
"harbor-ui": "0.2.87",
|
"harbor-ui": "0.3.2",
|
||||||
"intl": "^1.2.5",
|
"intl": "^1.2.5",
|
||||||
"mutationobserver-shim": "^0.3.2",
|
"mutationobserver-shim": "^0.3.2",
|
||||||
"ngx-cookie": "^1.0.0",
|
"ngx-cookie": "^1.0.0",
|
||||||
|
|
|
@ -273,7 +273,8 @@
|
||||||
"FOUND_ERROR_IN_JOBS": "Found errors in the replication job(s), please check.",
|
"FOUND_ERROR_IN_JOBS": "Found errors in the replication job(s), please check.",
|
||||||
"INVALID_DATE": "Invalid date.",
|
"INVALID_DATE": "Invalid date.",
|
||||||
"PLACEHOLDER": "We couldn't find any replication rules!",
|
"PLACEHOLDER": "We couldn't find any replication rules!",
|
||||||
"JOB_PLACEHOLDER": "We couldn't find any replication jobs!"
|
"JOB_PLACEHOLDER": "We couldn't find any replication jobs!",
|
||||||
|
"JOB_LOG_VIEWER": "View Replication Job Log"
|
||||||
},
|
},
|
||||||
"DESTINATION": {
|
"DESTINATION": {
|
||||||
"NEW_ENDPOINT": "New Endpoint",
|
"NEW_ENDPOINT": "New Endpoint",
|
||||||
|
@ -395,7 +396,7 @@
|
||||||
"TOKEN_EXPIRATION": "The expiration time (in minutes) of a token created by the token service. Default is 30 minutes.",
|
"TOKEN_EXPIRATION": "The expiration time (in minutes) of a token created by the token service. Default is 30 minutes.",
|
||||||
"PRO_CREATION_RESTRICTION": "The flag to define what users have permission to create projects. By default, everyone can create a project. Set to 'Admin Only' so that only an administrator can create a project.",
|
"PRO_CREATION_RESTRICTION": "The flag to define what users have permission to create projects. By default, everyone can create a project. Set to 'Admin Only' so that only an administrator can create a project.",
|
||||||
"ROOT_CERT_DOWNLOAD": "Download the root certificate of registry.",
|
"ROOT_CERT_DOWNLOAD": "Download the root certificate of registry.",
|
||||||
"SCANNING_POLICY": "Set image scanning policy based on different requirements. 'None': No active policy; 'Daily At': Triggering scanning at the specified time everyday; 'Upon Refresh': Triggering scanning when database refreshed."
|
"SCANNING_POLICY": "Set image scanning policy based on different requirements. 'None': No active policy; 'Daily At': Triggering scanning at the specified time everyday."
|
||||||
},
|
},
|
||||||
"LDAP": {
|
"LDAP": {
|
||||||
"URL": "LDAP URL",
|
"URL": "LDAP URL",
|
||||||
|
|
|
@ -273,7 +273,8 @@
|
||||||
"FOUND_ERROR_IN_JOBS": "Se han encontrado errores en el trabajo de replicación. Por favor, compruébelos.",
|
"FOUND_ERROR_IN_JOBS": "Se han encontrado errores en el trabajo de replicación. Por favor, compruébelos.",
|
||||||
"INVALID_DATE": "Fecha invalida.",
|
"INVALID_DATE": "Fecha invalida.",
|
||||||
"PLACEHOLDER": "We couldn't find any replication rules!",
|
"PLACEHOLDER": "We couldn't find any replication rules!",
|
||||||
"JOB_PLACEHOLDER": "We couldn't find any replication jobs!"
|
"JOB_PLACEHOLDER": "We couldn't find any replication jobs!",
|
||||||
|
"JOB_LOG_VIEWER": "View Replication Job Log"
|
||||||
},
|
},
|
||||||
"DESTINATION": {
|
"DESTINATION": {
|
||||||
"NEW_ENDPOINT": "Nuevo Endpoint",
|
"NEW_ENDPOINT": "Nuevo Endpoint",
|
||||||
|
@ -396,7 +397,7 @@
|
||||||
"TOKEN_EXPIRATION": "El tiempo de expiración (en minutos) del token creado por el servicio de tokens. Por defecto son 30 minutos.",
|
"TOKEN_EXPIRATION": "El tiempo de expiración (en minutos) del token creado por el servicio de tokens. Por defecto son 30 minutos.",
|
||||||
"PRO_CREATION_RESTRICTION": "Marca para definir qué usuarios tienen permisos para crear proyectos. Por defecto, todos pueden crear proyectos. Seleccione 'Solo Administradores' para que solamente los administradores puedan crear proyectos.",
|
"PRO_CREATION_RESTRICTION": "Marca para definir qué usuarios tienen permisos para crear proyectos. Por defecto, todos pueden crear proyectos. Seleccione 'Solo Administradores' para que solamente los administradores puedan crear proyectos.",
|
||||||
"ROOT_CERT_DOWNLOAD": "Download the root certificate of registry.",
|
"ROOT_CERT_DOWNLOAD": "Download the root certificate of registry.",
|
||||||
"SCANNING_POLICY": "Set image scanning policy based on different requirements. 'None': No active policy; 'Daily At': Triggering scanning at the specified time everyday; 'Upon Refresh': Triggering scanning when database refreshed."
|
"SCANNING_POLICY": "Set image scanning policy based on different requirements. 'None': No active policy; 'Daily At': Triggering scanning at the specified time everyday."
|
||||||
},
|
},
|
||||||
"LDAP": {
|
"LDAP": {
|
||||||
"URL": "LDAP URL",
|
"URL": "LDAP URL",
|
||||||
|
|
|
@ -273,7 +273,8 @@
|
||||||
"FOUND_ERROR_IN_JOBS": "复制任务中包含错误,请检查。",
|
"FOUND_ERROR_IN_JOBS": "复制任务中包含错误,请检查。",
|
||||||
"INVALID_DATE": "无效日期。",
|
"INVALID_DATE": "无效日期。",
|
||||||
"PLACEHOLDER": "未发现任何复制规则!",
|
"PLACEHOLDER": "未发现任何复制规则!",
|
||||||
"JOB_PLACEHOLDER": "未发现任何复制任务!"
|
"JOB_PLACEHOLDER": "未发现任何复制任务!",
|
||||||
|
"JOB_LOG_VIEWER": "查看复制任务日志"
|
||||||
},
|
},
|
||||||
"DESTINATION": {
|
"DESTINATION": {
|
||||||
"NEW_ENDPOINT": "新建目标",
|
"NEW_ENDPOINT": "新建目标",
|
||||||
|
@ -395,7 +396,7 @@
|
||||||
"TOKEN_EXPIRATION": "由令牌服务创建的令牌的过期时间(分钟),默认为30分钟。",
|
"TOKEN_EXPIRATION": "由令牌服务创建的令牌的过期时间(分钟),默认为30分钟。",
|
||||||
"PRO_CREATION_RESTRICTION": "用来确定哪些用户有权限创建项目,默认为’所有人‘,设置为’仅管理员‘则只有管理员可以创建项目。",
|
"PRO_CREATION_RESTRICTION": "用来确定哪些用户有权限创建项目,默认为’所有人‘,设置为’仅管理员‘则只有管理员可以创建项目。",
|
||||||
"ROOT_CERT_DOWNLOAD": "下载镜像库根证书.",
|
"ROOT_CERT_DOWNLOAD": "下载镜像库根证书.",
|
||||||
"SCANNING_POLICY": "基于不同需求设置镜像扫描策略。‘无’:不设置任何策略;‘每日定时’:每天在设置的时间定时执行扫描;‘缺陷库刷新后’:当缺陷数据库刷新后。"
|
"SCANNING_POLICY": "基于不同需求设置镜像扫描策略。‘无’:不设置任何策略;‘每日定时’:每天在设置的时间定时执行扫描。"
|
||||||
},
|
},
|
||||||
"LDAP": {
|
"LDAP": {
|
||||||
"URL": "LDAP URL",
|
"URL": "LDAP URL",
|
||||||
|
|
Loading…
Reference in New Issue
Block a user