mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-24 01:27:49 +01:00
commit
3120abad8e
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "harbor-ui",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.0",
|
||||
"description": "Harbor shared UI components based on Clarity and Angular4",
|
||||
"scripts": {
|
||||
"start": "ng serve --host 0.0.0.0 --port 4500 --proxy-config proxy.config.json",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "harbor-ui",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.0",
|
||||
"description": "Harbor shared UI components based on Clarity and Angular4",
|
||||
"author": "VMware",
|
||||
"module": "index.js",
|
||||
@ -43,4 +43,4 @@
|
||||
"web-animations-js": "^2.2.1",
|
||||
"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>
|
||||
<div class="form-group">
|
||||
<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>
|
||||
{{ updatedTimestamp }}
|
||||
<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">
|
||||
<option value="none">{{ 'CONFIG.SCANNING.NONE_POLICY' | translate }}</option>
|
||||
<option value="daily">{{ 'CONFIG.SCANNING.DAILY_POLICY' | translate }}</option>
|
||||
<option value="on_refresh">{{ 'CONFIG.SCANNING.REFRESH_POLICY' | translate }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<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";
|
||||
}
|
||||
|
||||
get isClairDBFullyReady(): boolean {
|
||||
return this.clairDBStatus && this.clairDBStatus.overall_last_update > 0;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private scanningService: ScanningResultService,
|
||||
private errorHandler: ErrorHandler,
|
||||
|
@ -20,6 +20,7 @@ import { ErrorHandler } from '../error-handler/error-handler';
|
||||
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||
import { ReplicationService, ReplicationDefaultService } from '../service/replication.service';
|
||||
import { EndpointService, EndpointDefaultService } from '../service/endpoint.service';
|
||||
import { JobLogViewerComponent } from '../job-log-viewer/job-log-viewer.component';
|
||||
|
||||
describe('CreateEditRuleComponent (inline template)', ()=>{
|
||||
|
||||
@ -175,7 +176,8 @@ describe('CreateEditRuleComponent (inline template)', ()=>{
|
||||
ConfirmationDialogComponent,
|
||||
DatePickerComponent,
|
||||
FilterComponent,
|
||||
InlineAlertComponent
|
||||
InlineAlertComponent,
|
||||
JobLogViewerComponent
|
||||
],
|
||||
providers: [
|
||||
ErrorHandler,
|
||||
|
@ -23,6 +23,7 @@ import { DATETIME_PICKER_DIRECTIVES } from './datetime-picker/index';
|
||||
import { VULNERABILITY_DIRECTIVES } from './vulnerability-scanning/index';
|
||||
import { PUSH_IMAGE_BUTTON_DIRECTIVES } from './push-image/index';
|
||||
import { CONFIGURATION_DIRECTIVES } from './config/index';
|
||||
import { JOB_LOG_VIEWER_DIRECTIVES } from './job-log-viewer/index';
|
||||
|
||||
import {
|
||||
SystemInfoService,
|
||||
@ -153,7 +154,8 @@ export function initConfig(translateInitializer: TranslateServiceInitializer, co
|
||||
DATETIME_PICKER_DIRECTIVES,
|
||||
VULNERABILITY_DIRECTIVES,
|
||||
PUSH_IMAGE_BUTTON_DIRECTIVES,
|
||||
CONFIGURATION_DIRECTIVES
|
||||
CONFIGURATION_DIRECTIVES,
|
||||
JOB_LOG_VIEWER_DIRECTIVES
|
||||
],
|
||||
exports: [
|
||||
LOG_DIRECTIVES,
|
||||
@ -173,6 +175,7 @@ export function initConfig(translateInitializer: TranslateServiceInitializer, co
|
||||
VULNERABILITY_DIRECTIVES,
|
||||
PUSH_IMAGE_BUTTON_DIRECTIVES,
|
||||
CONFIGURATION_DIRECTIVES,
|
||||
JOB_LOG_VIEWER_DIRECTIVES,
|
||||
TranslateModule
|
||||
],
|
||||
providers: []
|
||||
|
@ -15,4 +15,5 @@ export * from './vulnerability-scanning/index';
|
||||
export * from './i18n/index';
|
||||
export * from './push-image/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.update_time | date: 'short'}}</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>
|
||||
</a>
|
||||
</clr-dg-cell>
|
||||
@ -73,4 +73,5 @@ export const REPLICATION_TEMPLATE: string = `
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
<job-log-viewer #replicationLogViewer></job-log-viewer>
|
||||
</div>`;
|
@ -18,6 +18,7 @@ import { ErrorHandler } from '../error-handler/error-handler';
|
||||
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
|
||||
import { ReplicationService, ReplicationDefaultService } from '../service/replication.service';
|
||||
import { EndpointService, EndpointDefaultService } from '../service/endpoint.service';
|
||||
import { JobLogViewerComponent } from '../job-log-viewer/job-log-viewer.component';
|
||||
|
||||
describe('Replication Component (inline template)', ()=>{
|
||||
|
||||
@ -175,7 +176,8 @@ describe('Replication Component (inline template)', ()=>{
|
||||
ConfirmationDialogComponent,
|
||||
DatePickerComponent,
|
||||
FilterComponent,
|
||||
InlineAlertComponent
|
||||
InlineAlertComponent,
|
||||
JobLogViewerComponent
|
||||
],
|
||||
providers: [
|
||||
ErrorHandler,
|
||||
|
@ -17,7 +17,7 @@ import { NgModel } from '@angular/forms';
|
||||
|
||||
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 { ErrorHandler } from '../error-handler/error-handler';
|
||||
|
||||
@ -32,24 +32,26 @@ import { Comparator } from 'clarity-angular';
|
||||
import { REPLICATION_TEMPLATE } from './replication.component.html';
|
||||
import { REPLICATION_STYLE } from './replication.component.css';
|
||||
|
||||
const ruleStatus: {[key: string]: any} = [
|
||||
{ 'key': 'all', 'description': 'REPLICATION.ALL_STATUS'},
|
||||
{ 'key': '1', 'description': 'REPLICATION.ENABLED'},
|
||||
{ 'key': '0', 'description': 'REPLICATION.DISABLED'}
|
||||
import { JobLogViewerComponent } from '../job-log-viewer/index';
|
||||
|
||||
const ruleStatus: { [key: string]: any } = [
|
||||
{ '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': 'pending', 'description': 'REPLICATION.PENDING' },
|
||||
{ 'key': 'running', 'description': 'REPLICATION.RUNNING' },
|
||||
{ 'key': 'error', 'description': 'REPLICATION.ERROR' },
|
||||
{ 'key': 'pending', 'description': 'REPLICATION.PENDING' },
|
||||
{ 'key': 'running', 'description': 'REPLICATION.RUNNING' },
|
||||
{ 'key': 'error', 'description': 'REPLICATION.ERROR' },
|
||||
{ 'key': 'retrying', 'description': 'REPLICATION.RETRYING' },
|
||||
{ 'key': 'stopped' , 'description': 'REPLICATION.STOPPED' },
|
||||
{ 'key': 'stopped', 'description': 'REPLICATION.STOPPED' },
|
||||
{ '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 {
|
||||
ruleId: number | string;
|
||||
@ -67,163 +69,172 @@ export class SearchOption {
|
||||
@Component({
|
||||
selector: 'hbr-replication',
|
||||
template: REPLICATION_TEMPLATE,
|
||||
styles: [ REPLICATION_STYLE ]
|
||||
styles: [REPLICATION_STYLE]
|
||||
})
|
||||
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;
|
||||
currentRuleStatus: {key: string, description: string};
|
||||
search: SearchOption = new SearchOption();
|
||||
|
||||
jobStatus = jobStatus;
|
||||
currentJobStatus: {key: string, description: string};
|
||||
ruleStatus = ruleStatus;
|
||||
currentRuleStatus: { key: string, description: string };
|
||||
|
||||
changedRules: ReplicationRule[];
|
||||
initSelectedId: number | string;
|
||||
jobStatus = jobStatus;
|
||||
currentJobStatus: { key: string, description: string };
|
||||
|
||||
rules: ReplicationRule[];
|
||||
loading: boolean;
|
||||
changedRules: ReplicationRule[];
|
||||
initSelectedId: number | string;
|
||||
|
||||
jobs: ReplicationJob[];
|
||||
rules: ReplicationRule[];
|
||||
loading: boolean;
|
||||
|
||||
jobsTotalRecordCount: number;
|
||||
jobsTotalPage: number;
|
||||
jobs: ReplicationJob[];
|
||||
|
||||
toggleJobSearchOption = optionalSearch;
|
||||
currentJobSearchOption: number;
|
||||
jobsTotalRecordCount: number;
|
||||
jobsTotalPage: number;
|
||||
|
||||
@ViewChild(ListReplicationRuleComponent)
|
||||
listReplicationRule: ListReplicationRuleComponent;
|
||||
toggleJobSearchOption = optionalSearch;
|
||||
currentJobSearchOption: number;
|
||||
|
||||
@ViewChild(CreateEditRuleComponent)
|
||||
createEditPolicyComponent: CreateEditRuleComponent;
|
||||
@ViewChild(ListReplicationRuleComponent)
|
||||
listReplicationRule: ListReplicationRuleComponent;
|
||||
|
||||
creationTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('creation_time', 'date');
|
||||
updateTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('update_time', 'date');
|
||||
@ViewChild(CreateEditRuleComponent)
|
||||
createEditPolicyComponent: CreateEditRuleComponent;
|
||||
|
||||
constructor(
|
||||
private errorHandler: ErrorHandler,
|
||||
private replicationService: ReplicationService,
|
||||
private translateService: TranslateService) {
|
||||
}
|
||||
@ViewChild("replicationLogViewer")
|
||||
replicationLogViewer: JobLogViewerComponent;
|
||||
|
||||
ngOnInit() {
|
||||
this.currentRuleStatus = this.ruleStatus[0];
|
||||
this.currentJobStatus = this.jobStatus[0];
|
||||
this.currentJobSearchOption = 0;
|
||||
}
|
||||
creationTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('creation_time', 'date');
|
||||
updateTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('update_time', 'date');
|
||||
|
||||
openModal(): void {
|
||||
this.createEditPolicyComponent.openCreateEditRule(true);
|
||||
}
|
||||
constructor(
|
||||
private errorHandler: ErrorHandler,
|
||||
private replicationService: ReplicationService,
|
||||
private translateService: TranslateService) {
|
||||
}
|
||||
|
||||
openEditRule(rule: ReplicationRule) {
|
||||
if(rule) {
|
||||
let editable = true;
|
||||
if(rule.enabled === 1) {
|
||||
editable = false;
|
||||
}
|
||||
this.createEditPolicyComponent.openCreateEditRule(editable, rule.id);
|
||||
}
|
||||
}
|
||||
ngOnInit() {
|
||||
this.currentRuleStatus = this.ruleStatus[0];
|
||||
this.currentJobStatus = this.jobStatus[0];
|
||||
this.currentJobSearchOption = 0;
|
||||
}
|
||||
|
||||
fetchReplicationJobs() {
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
openModal(): void {
|
||||
this.createEditPolicyComponent.openCreateEditRule(true);
|
||||
}
|
||||
|
||||
selectOneRule(rule: ReplicationRule) {
|
||||
if (rule) {
|
||||
this.search.ruleId = rule.id || '';
|
||||
this.search.repoName = '';
|
||||
this.search.status = '';
|
||||
this.currentJobSearchOption = 0;
|
||||
this.currentJobStatus = { 'key': 'all', 'description': 'REPLICATION.ALL' };
|
||||
this.fetchReplicationJobs();
|
||||
}
|
||||
}
|
||||
openEditRule(rule: ReplicationRule) {
|
||||
if (rule) {
|
||||
let editable = true;
|
||||
if (rule.enabled === 1) {
|
||||
editable = false;
|
||||
}
|
||||
this.createEditPolicyComponent.openCreateEditRule(editable, rule.id);
|
||||
}
|
||||
}
|
||||
|
||||
customRedirect(rule: ReplicationRule) {
|
||||
this.redirect.emit(rule);
|
||||
}
|
||||
|
||||
doSearchRules(ruleName: string) {
|
||||
this.search.ruleName = ruleName;
|
||||
this.listReplicationRule.retrieveRules(ruleName);
|
||||
}
|
||||
fetchReplicationJobs() {
|
||||
|
||||
doFilterRuleStatus($event: any) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
doFilterJobStatus($event: any) {
|
||||
if ($event && $event.target && $event.target["value"]) {
|
||||
let status = $event.target["value"];
|
||||
|
||||
this.currentJobStatus = this.jobStatus.find((r: any)=>r.key === status);
|
||||
if(this.currentJobStatus.key === 'all') {
|
||||
status = '';
|
||||
}
|
||||
this.search.status = status;
|
||||
this.doSearchJobs(this.search.repoName);
|
||||
|
||||
}
|
||||
}
|
||||
toPromise<ReplicationJob[]>(this.replicationService
|
||||
.getJobs(this.search.ruleId, params))
|
||||
.then(
|
||||
response => {
|
||||
this.jobs = response;
|
||||
}).catch(error => {
|
||||
this.errorHandler.error(error);
|
||||
});
|
||||
}
|
||||
|
||||
doSearchJobs(repoName: string) {
|
||||
this.search.repoName = repoName;
|
||||
this.fetchReplicationJobs();
|
||||
}
|
||||
selectOneRule(rule: ReplicationRule) {
|
||||
if (rule) {
|
||||
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) {
|
||||
if(isReady) {
|
||||
this.search.ruleName = '';
|
||||
this.listReplicationRule.retrieveRules(this.search.ruleName);
|
||||
}
|
||||
}
|
||||
customRedirect(rule: ReplicationRule) {
|
||||
this.redirect.emit(rule);
|
||||
}
|
||||
|
||||
refreshRules() {
|
||||
this.listReplicationRule.retrieveRules();
|
||||
}
|
||||
doSearchRules(ruleName: string) {
|
||||
this.search.ruleName = ruleName;
|
||||
this.listReplicationRule.retrieveRules(ruleName);
|
||||
}
|
||||
|
||||
refreshJobs() {
|
||||
this.fetchReplicationJobs();
|
||||
}
|
||||
doFilterRuleStatus($event: any) {
|
||||
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) {
|
||||
(option === 1) ? this.currentJobSearchOption = 0 : this.currentJobSearchOption = 1;
|
||||
}
|
||||
doFilterJobStatus($event: any) {
|
||||
if ($event && $event.target && $event.target["value"]) {
|
||||
let status = $event.target["value"];
|
||||
|
||||
doJobSearchByStartTime(fromTimestamp: string) {
|
||||
this.search.startTimestamp = fromTimestamp;
|
||||
this.fetchReplicationJobs();
|
||||
}
|
||||
this.currentJobStatus = this.jobStatus.find((r: any) => r.key === status);
|
||||
if (this.currentJobStatus.key === 'all') {
|
||||
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
|
||||
*/
|
||||
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[])
|
||||
.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>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
<div *ngIf="hasNone" class="bar-summary-item">
|
||||
|
@ -31,7 +31,7 @@
|
||||
"clarity-icons": "^0.9.8",
|
||||
"clarity-ui": "^0.9.8",
|
||||
"core-js": "^2.4.1",
|
||||
"harbor-ui": "0.2.87",
|
||||
"harbor-ui": "0.3.2",
|
||||
"intl": "^1.2.5",
|
||||
"mutationobserver-shim": "^0.3.2",
|
||||
"ngx-cookie": "^1.0.0",
|
||||
|
@ -6,8 +6,8 @@
|
||||
<button [class.hide-create]="!canCreateUser" type="submit" class="btn btn-link custom-add-button" (click)="addNewUser()"><clr-icon shape="add"></clr-icon> {{'USER.ADD_ACTION' | translate}}</button>
|
||||
</span>
|
||||
<hbr-filter [withDivider]="true" class="filter-pos" filterPlaceholder='{{"USER.FILTER_PLACEHOLDER" | translate}}' (filter)="doFilter($event)" [currentValue]="currentTerm"></hbr-filter>
|
||||
<span class="refresh-btn" (click)="refresh()">
|
||||
<clr-icon shape="refresh" [hidden]="inProgress" ng-disabled="inProgress"></clr-icon>
|
||||
<span class="refresh-btn">
|
||||
<clr-icon shape="refresh" [hidden]="inProgress" ng-disabled="inProgress" (click)="refresh()"></clr-icon>
|
||||
<span class="spinner spinner-inline" [hidden]="inProgress === false"></span>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -60,6 +60,8 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
@ViewChild(NewUserModalComponent)
|
||||
newUserDialog: NewUserModalComponent;
|
||||
|
||||
timerHandler: any;
|
||||
|
||||
constructor(
|
||||
private userService: UserService,
|
||||
private translate: TranslateService,
|
||||
@ -75,8 +77,6 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
this.delUser(confirmed.data);
|
||||
}
|
||||
});
|
||||
let hnd = setInterval(() => ref.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 1000);
|
||||
}
|
||||
|
||||
isMySelf(uid: number): boolean {
|
||||
@ -126,12 +126,18 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.forceRefreshView(5000);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.deletionSubscription) {
|
||||
this.deletionSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
if (this.timerHandler) {
|
||||
clearInterval(this.timerHandler);
|
||||
this.timerHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
//Filter items by keywords
|
||||
@ -143,11 +149,10 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
} else {
|
||||
this.users = users.filter(user => {
|
||||
return this.isMatchFilterTerm(terms, user.username);
|
||||
})
|
||||
});
|
||||
this.forceRefreshView(5000);
|
||||
}
|
||||
});
|
||||
let hnd = setInterval(() => this.ref.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 1000);
|
||||
}
|
||||
|
||||
//Disable the admin role for the specified user
|
||||
@ -175,8 +180,7 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
.then(() => {
|
||||
//Change view now
|
||||
user.has_admin_role = updatedUser.has_admin_role;
|
||||
let hnd = setInterval(() => this.ref.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 1000);
|
||||
this.forceRefreshView(5000);
|
||||
})
|
||||
.catch(error => {
|
||||
this.msgHandler.handleError(error);
|
||||
@ -233,14 +237,15 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
this.totalCount = users.length;
|
||||
this.users = users.slice(from, to);//First page
|
||||
|
||||
this.forceRefreshView(5000);
|
||||
|
||||
return users;
|
||||
})
|
||||
.catch(error => {
|
||||
this.onGoing = false;
|
||||
this.msgHandler.handleError(error);
|
||||
this.forceRefreshView(5000);
|
||||
});
|
||||
let hnd = setInterval(() => this.ref.markForCheck(), 100);
|
||||
setTimeout(() => clearInterval(hnd), 1000);
|
||||
}
|
||||
|
||||
//Add new user
|
||||
@ -264,6 +269,7 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
this.originalUsers.then(users => {
|
||||
this.users = users.slice(state.page.from, state.page.to + 1);
|
||||
});
|
||||
this.forceRefreshView(5000);
|
||||
} else {
|
||||
this.refreshUser(state.page.from, state.page.to + 1);
|
||||
}
|
||||
@ -278,4 +284,18 @@ export class UserComponent implements OnInit, OnDestroy {
|
||||
this.refreshUser(0, 15);
|
||||
}
|
||||
|
||||
forceRefreshView(duration: number): void {
|
||||
//Reset timer
|
||||
if (this.timerHandler) {
|
||||
clearInterval(this.timerHandler);
|
||||
}
|
||||
this.timerHandler = setInterval(() => this.ref.markForCheck(), 100);
|
||||
setTimeout(() => {
|
||||
if (this.timerHandler) {
|
||||
clearInterval(this.timerHandler);
|
||||
this.timerHandler = null;
|
||||
}
|
||||
}, duration);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -273,7 +273,8 @@
|
||||
"FOUND_ERROR_IN_JOBS": "Found errors in the replication job(s), please check.",
|
||||
"INVALID_DATE": "Invalid date.",
|
||||
"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": {
|
||||
"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.",
|
||||
"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.",
|
||||
"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": {
|
||||
"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.",
|
||||
"INVALID_DATE": "Fecha invalida.",
|
||||
"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": {
|
||||
"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.",
|
||||
"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.",
|
||||
"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": {
|
||||
"URL": "LDAP URL",
|
||||
|
@ -277,7 +277,8 @@
|
||||
"FOUND_ERROR_IN_JOBS": "复制任务中包含错误,请检查。",
|
||||
"INVALID_DATE": "无效日期。",
|
||||
"PLACEHOLDER": "未发现任何复制规则!",
|
||||
"JOB_PLACEHOLDER": "未发现任何复制任务!"
|
||||
"JOB_PLACEHOLDER": "未发现任何复制任务!",
|
||||
"JOB_LOG_VIEWER": "查看复制任务日志"
|
||||
},
|
||||
"DESTINATION": {
|
||||
"NEW_ENDPOINT": "新建目标",
|
||||
@ -399,7 +400,7 @@
|
||||
"TOKEN_EXPIRATION": "由令牌服务创建的令牌的过期时间(分钟),默认为30分钟。",
|
||||
"PRO_CREATION_RESTRICTION": "用来确定哪些用户有权限创建项目,默认为’所有人‘,设置为’仅管理员‘则只有管理员可以创建项目。",
|
||||
"ROOT_CERT_DOWNLOAD": "下载镜像库根证书.",
|
||||
"SCANNING_POLICY": "基于不同需求设置镜像扫描策略。‘无’:不设置任何策略;‘每日定时’:每天在设置的时间定时执行扫描;‘缺陷库刷新后’:当缺陷数据库刷新后。"
|
||||
"SCANNING_POLICY": "基于不同需求设置镜像扫描策略。‘无’:不设置任何策略;‘每日定时’:每天在设置的时间定时执行扫描。"
|
||||
},
|
||||
"LDAP": {
|
||||
"URL": "LDAP URL",
|
||||
|
Loading…
Reference in New Issue
Block a user