2017-05-15 12:40:13 +02:00
|
|
|
// 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.
|
2023-03-17 11:52:23 +01:00
|
|
|
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
2022-09-20 11:16:16 +02:00
|
|
|
import { forkJoin, Observable, of, Subscription } from 'rxjs';
|
|
|
|
import { catchError, finalize, map } from 'rxjs/operators';
|
2022-05-13 10:00:45 +02:00
|
|
|
import { TranslateService } from '@ngx-translate/core';
|
|
|
|
import {
|
|
|
|
ClrDatagridComparatorInterface,
|
|
|
|
ClrDatagridStateInterface,
|
|
|
|
ClrLoadingState,
|
|
|
|
} from '@clr/angular';
|
|
|
|
import { ActivatedRoute, Router } from '@angular/router';
|
|
|
|
import { Comparator } from '../../../../../../../shared/services';
|
2017-07-20 03:28:00 +02:00
|
|
|
import {
|
2022-01-05 06:41:51 +01:00
|
|
|
calculatePage,
|
|
|
|
clone,
|
|
|
|
CustomComparator,
|
|
|
|
dbEncodeURIComponent,
|
|
|
|
DEFAULT_SUPPORTED_MIME_TYPES,
|
|
|
|
doSorting,
|
2022-05-13 10:00:45 +02:00
|
|
|
formatSize,
|
2023-03-14 07:41:16 +01:00
|
|
|
getHiddenArrayFromLocalStorage,
|
2022-05-13 10:00:45 +02:00
|
|
|
getPageSizeFromLocalStorage,
|
|
|
|
getSortingString,
|
|
|
|
PageSizeMapKeys,
|
2023-03-14 07:41:16 +01:00
|
|
|
setHiddenArrayToLocalStorage,
|
2022-05-13 10:00:45 +02:00
|
|
|
setPageSizeToLocalStorage,
|
|
|
|
VULNERABILITY_SCAN_STATUS,
|
|
|
|
} from '../../../../../../../shared/units/utils';
|
|
|
|
import { ErrorHandler } from '../../../../../../../shared/units/error-handler';
|
|
|
|
import { ArtifactService } from '../../../artifact.service';
|
|
|
|
import { OperationService } from '../../../../../../../shared/components/operation/operation.service';
|
|
|
|
import {
|
|
|
|
ConfirmationButtons,
|
|
|
|
ConfirmationState,
|
|
|
|
ConfirmationTargets,
|
|
|
|
} from '../../../../../../../shared/entities/shared.const';
|
|
|
|
import {
|
|
|
|
operateChanges,
|
|
|
|
OperateInfo,
|
|
|
|
OperationState,
|
|
|
|
} from '../../../../../../../shared/components/operation/operate';
|
2022-01-05 06:41:51 +01:00
|
|
|
import {
|
|
|
|
AccessoryType,
|
|
|
|
artifactDefault,
|
2022-09-20 11:16:16 +02:00
|
|
|
ArtifactFilterEvent,
|
2022-01-05 06:41:51 +01:00
|
|
|
ArtifactFront as Artifact,
|
|
|
|
ArtifactFront,
|
|
|
|
} from '../../../artifact';
|
2022-05-13 10:00:45 +02:00
|
|
|
import { Project } from '../../../../../project';
|
|
|
|
import { ArtifactService as NewArtifactService } from '../../../../../../../../../ng-swagger-gen/services/artifact.service';
|
|
|
|
import { ADDITIONS } from '../../../artifact-additions/models';
|
|
|
|
import { Platform } from '../../../../../../../../../ng-swagger-gen/models/platform';
|
2020-11-26 07:39:33 +01:00
|
|
|
import { SafeUrl } from '@angular/platform-browser';
|
2022-05-13 10:00:45 +02:00
|
|
|
import { errorHandler } from '../../../../../../../shared/units/shared.utils';
|
|
|
|
import { ConfirmationDialogComponent } from '../../../../../../../shared/components/confirmation-dialog';
|
|
|
|
import { ConfirmationMessage } from '../../../../../../global-confirmation-dialog/confirmation-message';
|
|
|
|
import { ConfirmationAcknowledgement } from '../../../../../../global-confirmation-dialog/confirmation-state-message';
|
|
|
|
import {
|
|
|
|
UN_LOGGED_PARAM,
|
|
|
|
YES,
|
|
|
|
} from '../../../../../../../account/sign-in/sign-in.service';
|
|
|
|
import { Label } from '../../../../../../../../../ng-swagger-gen/models/label';
|
|
|
|
import {
|
|
|
|
EventService,
|
|
|
|
HarborEvent,
|
|
|
|
} from '../../../../../../../services/event-service/event.service';
|
|
|
|
import { AppConfigService } from 'src/app/services/app-config.service';
|
|
|
|
import { ArtifactListPageService } from '../../artifact-list-page.service';
|
|
|
|
import { ACCESSORY_PAGE_SIZE } from './sub-accessories/sub-accessories.component';
|
|
|
|
import { Accessory } from 'ng-swagger-gen/models/accessory';
|
2022-01-10 11:14:34 +01:00
|
|
|
import { Tag } from '../../../../../../../../../ng-swagger-gen/models/tag';
|
2022-09-15 05:37:36 +02:00
|
|
|
import { CopyArtifactComponent } from './copy-artifact/copy-artifact.component';
|
|
|
|
import { CopyDigestComponent } from './copy-digest/copy-digest.component';
|
2021-03-15 08:56:34 +01:00
|
|
|
|
2020-02-13 08:39:29 +01:00
|
|
|
export const AVAILABLE_TIME = '0001-01-01T00:00:00.000Z';
|
2022-01-05 06:41:51 +01:00
|
|
|
|
|
|
|
const CHECKING: string = 'checking';
|
|
|
|
const TRUE: string = 'true';
|
|
|
|
const FALSE: string = 'false';
|
|
|
|
|
2017-05-15 12:40:13 +02:00
|
|
|
@Component({
|
2022-01-05 06:41:51 +01:00
|
|
|
selector: 'artifact-list-tab',
|
|
|
|
templateUrl: './artifact-list-tab.component.html',
|
2022-05-13 10:00:45 +02:00
|
|
|
styleUrls: ['./artifact-list-tab.component.scss'],
|
2017-05-15 12:40:13 +02:00
|
|
|
})
|
2023-03-17 11:52:23 +01:00
|
|
|
export class ArtifactListTabComponent implements OnInit, OnDestroy {
|
2022-01-05 06:41:51 +01:00
|
|
|
projectId: number;
|
|
|
|
projectName: string;
|
|
|
|
repoName: string;
|
|
|
|
registryUrl: string;
|
|
|
|
artifactList: ArtifactFront[] = [];
|
|
|
|
availableTime = AVAILABLE_TIME;
|
|
|
|
inprogress: boolean;
|
2022-05-13 10:00:45 +02:00
|
|
|
pullComparator: Comparator<Artifact> = new CustomComparator<Artifact>(
|
|
|
|
'pull_time',
|
|
|
|
'date'
|
|
|
|
);
|
|
|
|
pushComparator: Comparator<Artifact> = new CustomComparator<Artifact>(
|
|
|
|
'push_time',
|
|
|
|
'date'
|
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
|
|
|
|
loading = true;
|
|
|
|
selectedRow: Artifact[] = [];
|
|
|
|
|
2022-05-13 10:00:45 +02:00
|
|
|
@ViewChild('confirmationDialog')
|
2022-01-05 06:41:51 +01:00
|
|
|
confirmationDialog: ConfirmationDialogComponent;
|
|
|
|
|
2022-09-15 05:37:36 +02:00
|
|
|
@ViewChild(CopyArtifactComponent)
|
|
|
|
copyArtifactComponent: CopyArtifactComponent;
|
|
|
|
@ViewChild(CopyDigestComponent)
|
|
|
|
copyDigestComponent: CopyDigestComponent;
|
2022-05-13 10:00:45 +02:00
|
|
|
pageSize: number = getPageSizeFromLocalStorage(
|
|
|
|
PageSizeMapKeys.ARTIFACT_LIST_TAB_COMPONENT
|
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
currentPage = 1;
|
|
|
|
totalCount = 0;
|
|
|
|
currentState: ClrDatagridStateInterface;
|
|
|
|
|
|
|
|
get hasAddLabelImagePermission(): boolean {
|
|
|
|
return this.artifactListPageService.hasAddLabelImagePermission();
|
|
|
|
}
|
|
|
|
get hasRetagImagePermission(): boolean {
|
|
|
|
return this.artifactListPageService.hasRetagImagePermission();
|
|
|
|
}
|
|
|
|
get hasDeleteImagePermission(): boolean {
|
|
|
|
return this.artifactListPageService.hasDeleteImagePermission();
|
|
|
|
}
|
|
|
|
get hasScanImagePermission(): boolean {
|
|
|
|
return this.artifactListPageService.hasScanImagePermission();
|
|
|
|
}
|
|
|
|
get hasEnabledScanner(): boolean {
|
|
|
|
return this.artifactListPageService.hasEnabledScanner();
|
|
|
|
}
|
|
|
|
get scanBtnState(): ClrLoadingState {
|
|
|
|
return this.artifactListPageService.getScanBtnState();
|
|
|
|
}
|
|
|
|
onSendingScanCommand: boolean;
|
|
|
|
onSendingStopScanCommand: boolean = false;
|
|
|
|
onStopScanArtifactsLength: number = 0;
|
|
|
|
scanStoppedArtifactLength: number = 0;
|
|
|
|
artifactDigest: string;
|
|
|
|
depth: string;
|
|
|
|
// could Pagination filter
|
|
|
|
filters: string[];
|
|
|
|
scanFinishedArtifactLength: number = 0;
|
|
|
|
onScanArtifactsLength: number = 0;
|
|
|
|
stopBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
|
|
updateArtifactSub: Subscription;
|
2023-03-14 07:41:16 +01:00
|
|
|
|
|
|
|
hiddenArray: boolean[] = getHiddenArrayFromLocalStorage(
|
|
|
|
PageSizeMapKeys.ARTIFACT_LIST_TAB_COMPONENT,
|
2023-07-03 04:58:14 +02:00
|
|
|
[false, false, false, false, false, false, true, false, false, false]
|
2023-03-14 07:41:16 +01:00
|
|
|
);
|
2023-03-17 06:01:24 +01:00
|
|
|
deleteAccessorySub: Subscription;
|
2023-03-28 14:37:44 +02:00
|
|
|
copyDigestSub: Subscription;
|
2023-03-17 11:52:23 +01:00
|
|
|
@ViewChild('datagrid')
|
|
|
|
datagrid;
|
2022-01-05 06:41:51 +01:00
|
|
|
constructor(
|
|
|
|
private errorHandlerService: ErrorHandler,
|
|
|
|
private artifactService: ArtifactService,
|
|
|
|
private newArtifactService: NewArtifactService,
|
|
|
|
private translateService: TranslateService,
|
|
|
|
private operationService: OperationService,
|
|
|
|
private eventService: EventService,
|
|
|
|
private activatedRoute: ActivatedRoute,
|
|
|
|
private router: Router,
|
|
|
|
private appConfigService: AppConfigService,
|
2022-09-20 11:16:16 +02:00
|
|
|
private artifactListPageService: ArtifactListPageService
|
2023-03-17 11:52:23 +01:00
|
|
|
) {}
|
2022-07-29 13:04:01 +02:00
|
|
|
initRouterData() {
|
2022-05-13 10:00:45 +02:00
|
|
|
this.projectId =
|
|
|
|
this.activatedRoute.snapshot?.parent?.parent?.params['id'];
|
2022-07-29 13:04:01 +02:00
|
|
|
if (!this.projectId) {
|
|
|
|
this.errorHandlerService.error('Project ID cannot be unset.');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const resolverData = this.activatedRoute.snapshot?.parent?.parent?.data;
|
2022-01-05 06:41:51 +01:00
|
|
|
if (resolverData) {
|
|
|
|
this.projectName = (<Project>resolverData['projectResolver']).name;
|
|
|
|
}
|
|
|
|
this.repoName = this.activatedRoute.snapshot?.parent?.params['repo'];
|
2022-07-29 13:04:01 +02:00
|
|
|
if (!this.repoName) {
|
|
|
|
this.errorHandlerService.error('Repo name cannot be unset.');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.depth = this.activatedRoute.snapshot.params['depth'];
|
|
|
|
if (this.depth) {
|
|
|
|
const arr: string[] = this.depth.split('-');
|
|
|
|
this.artifactDigest = this.depth.split('-')[arr.length - 1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ngOnInit() {
|
2022-01-05 06:41:51 +01:00
|
|
|
this.registryUrl = this.appConfigService.getConfig().registry_url;
|
2022-07-29 13:04:01 +02:00
|
|
|
this.initRouterData();
|
2022-01-05 06:41:51 +01:00
|
|
|
if (!this.updateArtifactSub) {
|
2022-05-13 10:00:45 +02:00
|
|
|
this.updateArtifactSub = this.eventService.subscribe(
|
|
|
|
HarborEvent.UPDATE_VULNERABILITY_INFO,
|
|
|
|
(artifact: Artifact) => {
|
|
|
|
if (this.artifactList && this.artifactList.length) {
|
|
|
|
this.artifactList.forEach(item => {
|
|
|
|
if (item.digest === artifact.digest) {
|
|
|
|
item.scan_overview = artifact.scan_overview;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
2022-05-13 10:00:45 +02:00
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
2023-03-17 06:01:24 +01:00
|
|
|
if (!this.deleteAccessorySub) {
|
|
|
|
this.deleteAccessorySub = this.eventService.subscribe(
|
|
|
|
HarborEvent.DELETE_ACCESSORY,
|
|
|
|
(a: Accessory) => {
|
|
|
|
this.deleteAccessory(a);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2023-03-28 14:37:44 +02:00
|
|
|
if (!this.copyDigestSub) {
|
|
|
|
this.copyDigestSub = this.eventService.subscribe(
|
|
|
|
HarborEvent.COPY_DIGEST,
|
|
|
|
(a: Accessory) => {
|
|
|
|
this.copyDigestComponent.showDigestId(a.digest);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
2023-03-14 07:41:16 +01:00
|
|
|
|
2022-07-29 13:04:01 +02:00
|
|
|
ngOnDestroy() {
|
|
|
|
if (this.updateArtifactSub) {
|
|
|
|
this.updateArtifactSub.unsubscribe();
|
|
|
|
this.updateArtifactSub = null;
|
|
|
|
}
|
2023-03-17 06:01:24 +01:00
|
|
|
if (this.deleteAccessorySub) {
|
|
|
|
this.deleteAccessorySub.unsubscribe();
|
|
|
|
this.deleteAccessorySub = null;
|
|
|
|
}
|
2023-03-28 14:37:44 +02:00
|
|
|
if (this.copyDigestSub) {
|
|
|
|
this.copyDigestSub.unsubscribe();
|
|
|
|
this.copyDigestSub = null;
|
|
|
|
}
|
2023-03-17 11:52:23 +01:00
|
|
|
this.datagrid['columnsService']?.columns?.forEach((item, index) => {
|
|
|
|
if (this.depth) {
|
|
|
|
this.hiddenArray[index] = !!item?._value?.hidden;
|
|
|
|
} else {
|
|
|
|
if (index < 2) {
|
|
|
|
this.hiddenArray[index] = !!item?._value?.hidden;
|
|
|
|
} else {
|
|
|
|
this.hiddenArray[index + 1] = !!item?._value?.hidden;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
setHiddenArrayToLocalStorage(
|
|
|
|
PageSizeMapKeys.ARTIFACT_LIST_TAB_COMPONENT,
|
|
|
|
this.hiddenArray
|
|
|
|
);
|
2022-07-29 13:04:01 +02:00
|
|
|
}
|
2022-01-05 06:41:51 +01:00
|
|
|
|
|
|
|
clrDgRefresh(state: ClrDatagridStateInterface) {
|
|
|
|
setTimeout(() => {
|
2022-07-29 13:04:01 +02:00
|
|
|
//add setTimeout to avoid ng check error
|
2022-01-05 06:41:51 +01:00
|
|
|
this.clrLoad(state);
|
2022-07-29 13:04:01 +02:00
|
|
|
}, 0);
|
2020-02-20 09:12:46 +01:00
|
|
|
}
|
2022-01-05 06:41:51 +01:00
|
|
|
|
|
|
|
clrLoad(state: ClrDatagridStateInterface): void {
|
|
|
|
this.artifactList = [];
|
|
|
|
this.loading = true;
|
|
|
|
if (!state || !state.page) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.pageSize = state.page.size;
|
2022-05-13 10:00:45 +02:00
|
|
|
setPageSizeToLocalStorage(
|
|
|
|
PageSizeMapKeys.ARTIFACT_LIST_TAB_COMPONENT,
|
|
|
|
this.pageSize
|
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
this.selectedRow = [];
|
|
|
|
// Keep it for future filtering and sorting
|
|
|
|
|
|
|
|
let pageNumber: number = calculatePage(state);
|
|
|
|
if (pageNumber <= 0) {
|
|
|
|
pageNumber = 1;
|
|
|
|
}
|
|
|
|
let sortBy: any = '';
|
|
|
|
if (state.sort) {
|
2022-05-13 10:00:45 +02:00
|
|
|
sortBy = state.sort.by as
|
|
|
|
| string
|
|
|
|
| ClrDatagridComparatorInterface<any>;
|
2022-01-05 06:41:51 +01:00
|
|
|
sortBy = sortBy.fieldName ? sortBy.fieldName : sortBy;
|
|
|
|
sortBy = state.sort.reverse ? `-${sortBy}` : sortBy;
|
|
|
|
}
|
|
|
|
this.currentState = state;
|
|
|
|
|
|
|
|
// Pagination
|
|
|
|
let params: any = {};
|
|
|
|
if (pageNumber && this.pageSize) {
|
|
|
|
params.page = pageNumber;
|
|
|
|
params.pageSize = this.pageSize;
|
|
|
|
}
|
|
|
|
if (sortBy) {
|
|
|
|
params.sort = sortBy;
|
|
|
|
}
|
|
|
|
if (this.filters && this.filters.length) {
|
2022-05-13 10:00:45 +02:00
|
|
|
let q = '';
|
2022-01-05 06:41:51 +01:00
|
|
|
this.filters.forEach(item => {
|
|
|
|
q += item;
|
|
|
|
});
|
|
|
|
params.q = encodeURIComponent(q);
|
|
|
|
}
|
|
|
|
if (this.artifactDigest) {
|
|
|
|
const artifactParam: NewArtifactService.GetArtifactParams = {
|
|
|
|
repositoryName: dbEncodeURIComponent(this.repoName),
|
|
|
|
projectName: this.projectName,
|
|
|
|
reference: this.artifactDigest,
|
|
|
|
withImmutableStatus: true,
|
|
|
|
withLabel: true,
|
|
|
|
withScanOverview: true,
|
|
|
|
withTag: false,
|
|
|
|
XAcceptVulnerabilities: DEFAULT_SUPPORTED_MIME_TYPES,
|
2022-05-13 10:00:45 +02:00
|
|
|
withAccessory: false,
|
2022-01-05 06:41:51 +01:00
|
|
|
};
|
|
|
|
this.newArtifactService.getArtifact(artifactParam).subscribe(
|
|
|
|
res => {
|
|
|
|
let observableLists: Observable<Artifact>[] = [];
|
|
|
|
let platFormAttr: { platform: Platform }[] = [];
|
|
|
|
this.totalCount = res.references.length;
|
|
|
|
res.references.forEach((child, index) => {
|
2022-05-13 10:00:45 +02:00
|
|
|
if (
|
|
|
|
index >= (pageNumber - 1) * this.pageSize &&
|
|
|
|
index < pageNumber * this.pageSize
|
|
|
|
) {
|
|
|
|
let childParams: NewArtifactService.GetArtifactParams =
|
|
|
|
{
|
|
|
|
repositoryName: dbEncodeURIComponent(
|
|
|
|
this.repoName
|
|
|
|
),
|
|
|
|
projectName: this.projectName,
|
|
|
|
reference: child.child_digest,
|
|
|
|
withImmutableStatus: true,
|
|
|
|
withLabel: true,
|
|
|
|
withScanOverview: true,
|
|
|
|
withTag: false,
|
|
|
|
XAcceptVulnerabilities:
|
|
|
|
DEFAULT_SUPPORTED_MIME_TYPES,
|
|
|
|
withAccessory: false,
|
|
|
|
};
|
|
|
|
platFormAttr.push({ platform: child.platform });
|
|
|
|
observableLists.push(
|
|
|
|
this.newArtifactService.getArtifact(childParams)
|
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
});
|
2022-05-13 10:00:45 +02:00
|
|
|
forkJoin(observableLists)
|
|
|
|
.pipe(
|
|
|
|
finalize(() => {
|
|
|
|
this.loading = false;
|
|
|
|
})
|
|
|
|
)
|
|
|
|
.subscribe(
|
|
|
|
artifacts => {
|
|
|
|
this.artifactList = artifacts;
|
|
|
|
this.artifactList = doSorting<ArtifactFront>(
|
|
|
|
this.artifactList,
|
|
|
|
state
|
|
|
|
);
|
|
|
|
this.artifactList.forEach((artifact, index) => {
|
|
|
|
artifact.platform = clone(
|
|
|
|
platFormAttr[index].platform
|
|
|
|
);
|
|
|
|
});
|
|
|
|
this.getArtifactTagsAsync(this.artifactList);
|
|
|
|
this.getAccessoriesAsync(this.artifactList);
|
|
|
|
this.checkCosignAsync(this.artifactList);
|
|
|
|
this.getIconsFromBackEnd();
|
|
|
|
},
|
|
|
|
error => {
|
|
|
|
this.errorHandlerService.error(error);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
},
|
|
|
|
error => {
|
2022-01-05 06:41:51 +01:00
|
|
|
this.loading = false;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
let listArtifactParams: NewArtifactService.ListArtifactsParams = {
|
|
|
|
projectName: this.projectName,
|
|
|
|
repositoryName: dbEncodeURIComponent(this.repoName),
|
|
|
|
withLabel: true,
|
|
|
|
withScanOverview: true,
|
|
|
|
withTag: false,
|
|
|
|
sort: getSortingString(state),
|
|
|
|
XAcceptVulnerabilities: DEFAULT_SUPPORTED_MIME_TYPES,
|
2022-05-13 10:00:45 +02:00
|
|
|
withAccessory: false,
|
2022-01-05 06:41:51 +01:00
|
|
|
};
|
|
|
|
Object.assign(listArtifactParams, params);
|
2022-05-13 10:00:45 +02:00
|
|
|
this.newArtifactService
|
|
|
|
.listArtifactsResponse(listArtifactParams)
|
|
|
|
.pipe(finalize(() => (this.loading = false)))
|
|
|
|
.subscribe(
|
|
|
|
res => {
|
|
|
|
if (res.headers) {
|
|
|
|
let xHeader: string =
|
|
|
|
res.headers.get('X-Total-Count');
|
|
|
|
if (xHeader) {
|
|
|
|
this.totalCount = parseInt(xHeader, 0);
|
|
|
|
}
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
2022-05-13 10:00:45 +02:00
|
|
|
this.artifactList = res.body;
|
|
|
|
this.getArtifactTagsAsync(this.artifactList);
|
|
|
|
this.getAccessoriesAsync(this.artifactList);
|
|
|
|
this.checkCosignAsync(this.artifactList);
|
|
|
|
this.getIconsFromBackEnd();
|
|
|
|
},
|
|
|
|
error => {
|
|
|
|
// error
|
|
|
|
this.errorHandlerService.error(error);
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
2022-05-13 10:00:45 +02:00
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
refresh() {
|
|
|
|
this.currentPage = 1;
|
|
|
|
let st: ClrDatagridStateInterface = this.currentState;
|
|
|
|
if (!st) {
|
2022-05-13 10:00:45 +02:00
|
|
|
st = { page: {} };
|
2022-01-05 06:41:51 +01:00
|
|
|
st.page.size = this.pageSize;
|
|
|
|
st.page.from = 0;
|
|
|
|
st.page.to = this.pageSize - 1;
|
|
|
|
}
|
|
|
|
this.clrLoad(st);
|
|
|
|
}
|
|
|
|
|
|
|
|
canAddLabel(): boolean {
|
|
|
|
if (this.selectedRow && this.selectedRow.length === 1) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (this.selectedRow && this.selectedRow.length > 1) {
|
|
|
|
for (let i = 0; i < this.selectedRow.length; i++) {
|
2022-05-13 10:00:45 +02:00
|
|
|
if (
|
|
|
|
this.selectedRow[i].labels &&
|
|
|
|
this.selectedRow[i].labels.length
|
|
|
|
) {
|
2022-01-05 06:41:51 +01:00
|
|
|
return false;
|
|
|
|
}
|
2020-02-13 08:39:29 +01:00
|
|
|
}
|
2022-01-05 06:41:51 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-09-20 11:16:16 +02:00
|
|
|
stickLabel(labelEvent: { label: Label; isAdd: boolean }): void {
|
|
|
|
if (labelEvent.isAdd) {
|
|
|
|
this.addLabel(labelEvent?.label);
|
|
|
|
} else {
|
|
|
|
this.removeLabel(labelEvent?.label);
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
}
|
2022-09-20 11:16:16 +02:00
|
|
|
addLabel(label: Label) {
|
2022-05-13 10:00:45 +02:00
|
|
|
if (!this.inprogress) {
|
2022-09-20 11:16:16 +02:00
|
|
|
const params: NewArtifactService.AddLabelParams = {
|
|
|
|
projectName: this.projectName,
|
|
|
|
repositoryName: dbEncodeURIComponent(this.repoName),
|
|
|
|
reference: this.selectedRow[0].digest,
|
|
|
|
label: label,
|
|
|
|
};
|
2022-01-05 06:41:51 +01:00
|
|
|
this.inprogress = true;
|
2022-09-20 11:16:16 +02:00
|
|
|
this.newArtifactService
|
|
|
|
.addLabel(params)
|
2022-05-13 10:00:45 +02:00
|
|
|
.pipe(finalize(() => (this.inprogress = false)))
|
2022-09-20 11:16:16 +02:00
|
|
|
.subscribe({
|
|
|
|
next: res => {
|
2022-05-13 10:00:45 +02:00
|
|
|
this.refresh();
|
|
|
|
},
|
2022-09-20 11:16:16 +02:00
|
|
|
error: err => {
|
2022-05-13 10:00:45 +02:00
|
|
|
this.refresh();
|
|
|
|
this.errorHandlerService.error(err);
|
2022-09-20 11:16:16 +02:00
|
|
|
},
|
|
|
|
});
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
}
|
2022-09-20 11:16:16 +02:00
|
|
|
removeLabel(label: Label) {
|
2022-01-05 06:41:51 +01:00
|
|
|
if (!this.inprogress) {
|
|
|
|
let params: NewArtifactService.RemoveLabelParams = {
|
|
|
|
projectName: this.projectName,
|
|
|
|
repositoryName: dbEncodeURIComponent(this.repoName),
|
|
|
|
reference: this.selectedRow[0].digest,
|
2022-09-20 11:16:16 +02:00
|
|
|
labelId: label.id,
|
2022-01-05 06:41:51 +01:00
|
|
|
};
|
2022-09-20 11:16:16 +02:00
|
|
|
this.inprogress = true;
|
|
|
|
this.newArtifactService
|
|
|
|
.removeLabel(params)
|
|
|
|
.pipe(finalize(() => (this.inprogress = false)))
|
|
|
|
.subscribe({
|
|
|
|
next: res => {
|
|
|
|
this.refresh();
|
|
|
|
},
|
|
|
|
error: err => {
|
|
|
|
this.refresh();
|
|
|
|
this.errorHandlerService.error(err);
|
|
|
|
},
|
|
|
|
});
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sizeTransform(tagSize: string): string {
|
|
|
|
return formatSize(tagSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
retag() {
|
|
|
|
if (this.selectedRow && this.selectedRow.length && !this.depth) {
|
2022-09-15 05:37:36 +02:00
|
|
|
this.copyArtifactComponent.retag(this.selectedRow[0].digest);
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
deleteArtifact() {
|
|
|
|
if (this.selectedRow && this.selectedRow.length && !this.depth) {
|
|
|
|
let artifactNames: string[] = [];
|
|
|
|
this.selectedRow.forEach(artifact => {
|
|
|
|
artifactNames.push(artifact.digest.slice(0, 15));
|
|
|
|
});
|
|
|
|
|
2022-05-13 10:00:45 +02:00
|
|
|
let titleKey: string,
|
|
|
|
summaryKey: string,
|
|
|
|
content: string,
|
|
|
|
buttons: ConfirmationButtons;
|
|
|
|
titleKey = 'REPOSITORY.DELETION_TITLE_ARTIFACT';
|
|
|
|
summaryKey = 'REPOSITORY.DELETION_SUMMARY_ARTIFACT';
|
2022-01-05 06:41:51 +01:00
|
|
|
buttons = ConfirmationButtons.DELETE_CANCEL;
|
2022-05-13 10:00:45 +02:00
|
|
|
content = artifactNames.join(' , ');
|
2022-01-05 06:41:51 +01:00
|
|
|
let message = new ConfirmationMessage(
|
|
|
|
titleKey,
|
|
|
|
summaryKey,
|
|
|
|
content,
|
|
|
|
this.selectedRow,
|
|
|
|
ConfirmationTargets.TAG,
|
2022-05-13 10:00:45 +02:00
|
|
|
buttons
|
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
this.confirmationDialog.open(message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
deleteArtifactobservableLists: Observable<any>[] = [];
|
|
|
|
|
|
|
|
confirmDeletion(message: ConfirmationAcknowledgement) {
|
2022-05-13 10:00:45 +02:00
|
|
|
if (
|
|
|
|
message &&
|
|
|
|
message.source === ConfirmationTargets.ACCESSORY &&
|
|
|
|
message.state === ConfirmationState.CONFIRMED
|
|
|
|
) {
|
|
|
|
// delete one accessory
|
2022-01-05 06:41:51 +01:00
|
|
|
this.loading = true;
|
|
|
|
// init operation info
|
|
|
|
const opeMessage = new OperateInfo();
|
|
|
|
opeMessage.name = 'ACCESSORY.DELETE_ACCESSORY';
|
|
|
|
opeMessage.data.id = message.data.id;
|
|
|
|
opeMessage.state = OperationState.progressing;
|
|
|
|
opeMessage.data.name = message.data.digest.slice(0, 15);
|
|
|
|
this.operationService.publishInfo(opeMessage);
|
|
|
|
const params: NewArtifactService.DeleteArtifactParams = {
|
|
|
|
projectName: this.projectName,
|
2022-01-17 07:19:19 +01:00
|
|
|
repositoryName: dbEncodeURIComponent(this.repoName),
|
2022-01-05 06:41:51 +01:00
|
|
|
reference: message.data.digest,
|
|
|
|
};
|
2022-05-13 10:00:45 +02:00
|
|
|
this.newArtifactService
|
|
|
|
.deleteArtifact(params)
|
|
|
|
.pipe(finalize(() => (this.loading = false)))
|
2022-01-05 06:41:51 +01:00
|
|
|
.subscribe(
|
2022-05-13 10:00:45 +02:00
|
|
|
res => {
|
|
|
|
this.errorHandlerService.info(
|
|
|
|
'ACCESSORY.DELETED_SUCCESS'
|
|
|
|
);
|
|
|
|
operateChanges(opeMessage, OperationState.success);
|
|
|
|
this.refresh();
|
|
|
|
},
|
|
|
|
error => {
|
|
|
|
this.errorHandlerService.error(error);
|
|
|
|
operateChanges(
|
|
|
|
opeMessage,
|
|
|
|
OperationState.failure,
|
|
|
|
errorHandler(error)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
2022-05-13 10:00:45 +02:00
|
|
|
if (
|
|
|
|
message &&
|
|
|
|
message.source === ConfirmationTargets.TAG &&
|
|
|
|
message.state === ConfirmationState.CONFIRMED
|
|
|
|
) {
|
2022-01-05 06:41:51 +01:00
|
|
|
let artifactList = message.data;
|
|
|
|
if (artifactList && artifactList.length) {
|
|
|
|
artifactList.forEach(artifact => {
|
2022-05-13 10:00:45 +02:00
|
|
|
this.deleteArtifactobservableLists.push(
|
|
|
|
this.delOperate(artifact)
|
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
});
|
|
|
|
this.loading = true;
|
2022-05-13 10:00:45 +02:00
|
|
|
forkJoin(...this.deleteArtifactobservableLists).subscribe(
|
|
|
|
deleteResult => {
|
|
|
|
let deleteSuccessList = [];
|
|
|
|
let deleteErrorList = [];
|
|
|
|
this.deleteArtifactobservableLists = [];
|
|
|
|
deleteResult.forEach(result => {
|
|
|
|
if (!result) {
|
|
|
|
// delete success
|
|
|
|
deleteSuccessList.push(result);
|
|
|
|
} else {
|
|
|
|
deleteErrorList.push(result);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
this.selectedRow = [];
|
|
|
|
if (deleteSuccessList.length === deleteResult.length) {
|
|
|
|
// all is success
|
|
|
|
let st: ClrDatagridStateInterface = {
|
|
|
|
page: {
|
|
|
|
from: 0,
|
|
|
|
to: this.pageSize - 1,
|
|
|
|
size: this.pageSize,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
this.clrLoad(st);
|
|
|
|
} else if (
|
|
|
|
deleteErrorList.length === deleteResult.length
|
|
|
|
) {
|
|
|
|
// all is error
|
|
|
|
this.loading = false;
|
|
|
|
this.errorHandlerService.error(
|
|
|
|
deleteResult[deleteResult.length - 1]
|
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
} else {
|
2022-05-13 10:00:45 +02:00
|
|
|
// some artifact delete success but it has error delete things
|
|
|
|
this.errorHandlerService.error(
|
|
|
|
deleteErrorList[deleteErrorList.length - 1]
|
|
|
|
);
|
|
|
|
// if delete one success refresh list
|
|
|
|
let st: ClrDatagridStateInterface = {
|
|
|
|
page: {
|
|
|
|
from: 0,
|
|
|
|
to: this.pageSize - 1,
|
|
|
|
size: this.pageSize,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
this.clrLoad(st);
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
}
|
2022-05-13 10:00:45 +02:00
|
|
|
);
|
2021-03-15 08:56:34 +01:00
|
|
|
}
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
delOperate(artifact: Artifact): Observable<any> | null {
|
|
|
|
// init operation info
|
|
|
|
let operMessage = new OperateInfo();
|
|
|
|
operMessage.name = 'OPERATION.DELETE_TAG';
|
|
|
|
operMessage.data.id = artifact.id;
|
|
|
|
operMessage.state = OperationState.progressing;
|
|
|
|
operMessage.data.name = artifact.digest;
|
|
|
|
this.operationService.publishInfo(operMessage);
|
|
|
|
let params: NewArtifactService.DeleteArtifactParams = {
|
|
|
|
projectName: this.projectName,
|
|
|
|
repositoryName: dbEncodeURIComponent(this.repoName),
|
2022-05-13 10:00:45 +02:00
|
|
|
reference: artifact.digest,
|
2022-01-05 06:41:51 +01:00
|
|
|
};
|
2022-05-13 10:00:45 +02:00
|
|
|
return this.newArtifactService.deleteArtifact(params).pipe(
|
|
|
|
map(response => {
|
|
|
|
this.translateService
|
|
|
|
.get('BATCH.DELETED_SUCCESS')
|
|
|
|
.subscribe(res => {
|
|
|
|
operateChanges(operMessage, OperationState.success);
|
|
|
|
});
|
|
|
|
}),
|
|
|
|
catchError(error => {
|
2022-01-05 06:41:51 +01:00
|
|
|
const message = errorHandler(error);
|
2022-05-13 10:00:45 +02:00
|
|
|
this.translateService
|
|
|
|
.get(message)
|
|
|
|
.subscribe(res =>
|
|
|
|
operateChanges(operMessage, OperationState.failure, res)
|
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
return of(error);
|
2022-05-13 10:00:45 +02:00
|
|
|
})
|
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
showDigestId() {
|
2022-05-13 10:00:45 +02:00
|
|
|
if (this.selectedRow && this.selectedRow.length === 1 && !this.depth) {
|
2022-09-15 05:37:36 +02:00
|
|
|
this.copyDigestComponent.showDigestId(this.selectedRow[0].digest);
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
goIntoArtifactSummaryPage(artifact: Artifact): void {
|
|
|
|
const relativeRouterLink: string[] = ['artifacts', artifact.digest];
|
|
|
|
if (this.activatedRoute.snapshot.queryParams[UN_LOGGED_PARAM] === YES) {
|
2022-05-13 10:00:45 +02:00
|
|
|
this.router.navigate(relativeRouterLink, {
|
|
|
|
relativeTo: this.activatedRoute,
|
|
|
|
queryParams: { [UN_LOGGED_PARAM]: YES },
|
|
|
|
});
|
2022-01-05 06:41:51 +01:00
|
|
|
} else {
|
2022-05-13 10:00:45 +02:00
|
|
|
this.router.navigate(relativeRouterLink, {
|
|
|
|
relativeTo: this.activatedRoute,
|
|
|
|
});
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get vulnerability scanning status
|
|
|
|
scanStatus(artifact: Artifact): string {
|
|
|
|
if (artifact) {
|
|
|
|
let so = this.handleScanOverview((<any>artifact).scan_overview);
|
|
|
|
if (so && so.scan_status) {
|
|
|
|
return so.scan_status;
|
2020-03-16 11:59:21 +01:00
|
|
|
}
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
return VULNERABILITY_SCAN_STATUS.NOT_SCANNED;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if has running job, return false
|
|
|
|
canScanNow(): boolean {
|
|
|
|
if (!this.hasScanImagePermission) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (this.onSendingScanCommand) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (this.selectedRow && this.selectedRow.length) {
|
|
|
|
let flag: boolean = true;
|
|
|
|
this.selectedRow.forEach(item => {
|
|
|
|
const st: string = this.scanStatus(item);
|
|
|
|
if (this.isRunningState(st)) {
|
|
|
|
flag = false;
|
|
|
|
}
|
2020-02-13 08:39:29 +01:00
|
|
|
});
|
2022-01-05 06:41:51 +01:00
|
|
|
return flag;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Trigger scan
|
|
|
|
scanNow(): void {
|
|
|
|
if (!this.selectedRow.length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.scanFinishedArtifactLength = 0;
|
|
|
|
this.onScanArtifactsLength = this.selectedRow.length;
|
|
|
|
this.onSendingScanCommand = true;
|
|
|
|
this.selectedRow.forEach((data: any) => {
|
|
|
|
let digest = data.digest;
|
2022-05-13 10:00:45 +02:00
|
|
|
this.eventService.publish(
|
|
|
|
HarborEvent.START_SCAN_ARTIFACT,
|
|
|
|
this.repoName + '/' + digest
|
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
selectedRowHasVul(): boolean {
|
2022-05-13 10:00:45 +02:00
|
|
|
return !!(
|
|
|
|
this.selectedRow &&
|
|
|
|
this.selectedRow[0] &&
|
|
|
|
this.selectedRow[0].addition_links &&
|
|
|
|
this.selectedRow[0].addition_links[ADDITIONS.VULNERABILITIES]
|
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
hasVul(artifact: Artifact): boolean {
|
2022-05-13 10:00:45 +02:00
|
|
|
return !!(
|
|
|
|
artifact &&
|
|
|
|
artifact.addition_links &&
|
|
|
|
artifact.addition_links[ADDITIONS.VULNERABILITIES]
|
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
submitFinish(e: boolean) {
|
|
|
|
this.scanFinishedArtifactLength += 1;
|
|
|
|
// all selected scan action has started
|
|
|
|
if (this.scanFinishedArtifactLength === this.onScanArtifactsLength) {
|
|
|
|
this.onSendingScanCommand = e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
submitStopFinish(e: boolean) {
|
|
|
|
this.scanStoppedArtifactLength += 1;
|
|
|
|
// all selected scan action has stopped
|
|
|
|
if (this.scanStoppedArtifactLength === this.onStopScanArtifactsLength) {
|
|
|
|
this.onSendingScanCommand = e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
handleScanOverview(scanOverview: any): any {
|
|
|
|
if (scanOverview) {
|
|
|
|
return Object.values(scanOverview)[0];
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
goIntoIndexArtifact(artifact: Artifact) {
|
|
|
|
let depth: string = '';
|
|
|
|
if (this.depth) {
|
|
|
|
depth = this.depth + '-' + artifact.digest;
|
2020-02-13 08:39:29 +01:00
|
|
|
} else {
|
2022-01-05 06:41:51 +01:00
|
|
|
depth = artifact.digest;
|
|
|
|
}
|
2022-05-13 10:00:45 +02:00
|
|
|
const linkUrl = [
|
|
|
|
'harbor',
|
|
|
|
'projects',
|
|
|
|
this.projectId,
|
|
|
|
'repositories',
|
|
|
|
this.repoName,
|
|
|
|
'artifacts-tab',
|
|
|
|
'depth',
|
|
|
|
depth,
|
|
|
|
];
|
2022-01-05 06:41:51 +01:00
|
|
|
if (this.activatedRoute.snapshot.queryParams[UN_LOGGED_PARAM] === YES) {
|
2022-05-13 10:00:45 +02:00
|
|
|
this.router.navigate(linkUrl, {
|
|
|
|
queryParams: { [UN_LOGGED_PARAM]: YES },
|
|
|
|
});
|
2022-01-05 06:41:51 +01:00
|
|
|
} else {
|
|
|
|
this.router.navigate(linkUrl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-20 11:16:16 +02:00
|
|
|
filterEvent(e: ArtifactFilterEvent) {
|
2022-01-05 06:41:51 +01:00
|
|
|
this.filters = [];
|
2022-09-20 11:16:16 +02:00
|
|
|
if (e?.isLabel) {
|
|
|
|
if (e?.label?.name) {
|
|
|
|
this.filters.push(`${e.type}=(${e?.label?.id})`);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (e?.stringValue) {
|
2023-02-08 09:00:37 +01:00
|
|
|
if (e?.isInputTag) {
|
|
|
|
// for input tag, use fuzzy match
|
|
|
|
this.filters.push(`${e.type}=~${e?.stringValue}`);
|
|
|
|
} else {
|
|
|
|
this.filters.push(`${e.type}=${e?.stringValue}`);
|
|
|
|
}
|
2022-09-20 11:16:16 +02:00
|
|
|
}
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
2022-09-20 11:16:16 +02:00
|
|
|
this.refresh();
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
// when finished, remove it from selectedRow
|
|
|
|
scanFinished(artifact: Artifact) {
|
|
|
|
if (this.selectedRow && this.selectedRow.length) {
|
|
|
|
for (let i = 0; i < this.selectedRow.length; i++) {
|
|
|
|
if (artifact.digest === this.selectedRow[i].digest) {
|
|
|
|
this.selectedRow.splice(i, 1);
|
|
|
|
break;
|
2020-11-26 07:39:33 +01:00
|
|
|
}
|
|
|
|
}
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getIconsFromBackEnd() {
|
|
|
|
if (this.artifactList && this.artifactList.length) {
|
|
|
|
this.artifactService.getIconsFromBackEnd(this.artifactList);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
showDefaultIcon(event: any) {
|
|
|
|
if (event && event.target) {
|
|
|
|
event.target.src = artifactDefault;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getIcon(icon: string): SafeUrl {
|
|
|
|
return this.artifactService.getIcon(icon);
|
|
|
|
}
|
|
|
|
|
|
|
|
// get Tags and display less than 9 tags(too many tags will make UI stuck)
|
|
|
|
getArtifactTagsAsync(artifacts: ArtifactFront[]) {
|
|
|
|
if (artifacts && artifacts.length) {
|
|
|
|
artifacts.forEach(item => {
|
|
|
|
const listTagParams: NewArtifactService.ListTagsParams = {
|
|
|
|
projectName: this.projectName,
|
|
|
|
repositoryName: dbEncodeURIComponent(this.repoName),
|
|
|
|
reference: item.digest,
|
|
|
|
page: 1,
|
2022-05-13 10:00:45 +02:00
|
|
|
pageSize: 8,
|
2022-01-05 06:41:51 +01:00
|
|
|
};
|
2022-05-13 10:00:45 +02:00
|
|
|
this.newArtifactService
|
|
|
|
.listTagsResponse(listTagParams)
|
|
|
|
.subscribe(res => {
|
2022-01-05 06:41:51 +01:00
|
|
|
if (res.headers) {
|
2022-05-13 10:00:45 +02:00
|
|
|
let xHeader: string =
|
|
|
|
res.headers.get('x-total-count');
|
2022-01-05 06:41:51 +01:00
|
|
|
if (xHeader) {
|
|
|
|
item.tagNumber = Number.parseInt(xHeader, 10);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
item.tags = res.body;
|
2022-05-13 10:00:45 +02:00
|
|
|
});
|
2022-01-05 06:41:51 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// get accessories
|
|
|
|
getAccessoriesAsync(artifacts: ArtifactFront[]) {
|
|
|
|
if (artifacts && artifacts.length) {
|
|
|
|
artifacts.forEach(item => {
|
2022-05-13 10:00:45 +02:00
|
|
|
const listTagParams: NewArtifactService.ListAccessoriesParams =
|
|
|
|
{
|
|
|
|
projectName: this.projectName,
|
|
|
|
repositoryName: dbEncodeURIComponent(this.repoName),
|
|
|
|
reference: item.digest,
|
|
|
|
page: 1,
|
|
|
|
pageSize: ACCESSORY_PAGE_SIZE,
|
|
|
|
};
|
|
|
|
this.newArtifactService
|
|
|
|
.listAccessoriesResponse(listTagParams)
|
|
|
|
.subscribe(res => {
|
2022-01-05 06:41:51 +01:00
|
|
|
if (res.headers) {
|
2022-05-13 10:00:45 +02:00
|
|
|
let xHeader: string =
|
|
|
|
res.headers.get('x-total-count');
|
2022-01-05 06:41:51 +01:00
|
|
|
if (xHeader) {
|
2022-05-13 10:00:45 +02:00
|
|
|
item.accessoryNumber = Number.parseInt(
|
|
|
|
xHeader,
|
|
|
|
10
|
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
item.accessories = res.body;
|
2022-05-13 10:00:45 +02:00
|
|
|
});
|
2022-01-05 06:41:51 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
checkCosignAsync(artifacts: ArtifactFront[]) {
|
2023-07-19 12:22:11 +02:00
|
|
|
if (artifacts) {
|
|
|
|
if (artifacts.length) {
|
|
|
|
artifacts.forEach(item => {
|
2023-08-03 07:06:15 +02:00
|
|
|
item.signed = CHECKING;
|
|
|
|
this.newArtifactService
|
|
|
|
.listAccessories({
|
2023-07-19 12:22:11 +02:00
|
|
|
projectName: this.projectName,
|
|
|
|
repositoryName: dbEncodeURIComponent(this.repoName),
|
|
|
|
reference: item.digest,
|
|
|
|
page: 1,
|
|
|
|
pageSize: ACCESSORY_PAGE_SIZE,
|
2023-07-20 19:07:22 +02:00
|
|
|
q: encodeURIComponent(
|
2023-08-03 07:06:15 +02:00
|
|
|
`type={${AccessoryType.COSIGN} ${AccessoryType.NOTATION}}`
|
2023-07-20 19:07:22 +02:00
|
|
|
),
|
2023-08-03 07:06:15 +02:00
|
|
|
})
|
|
|
|
.subscribe({
|
|
|
|
next: res => {
|
|
|
|
if (res?.length) {
|
|
|
|
item.signed = TRUE;
|
|
|
|
} else {
|
|
|
|
item.signed = FALSE;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
error: err => {
|
|
|
|
item.signed = FALSE;
|
|
|
|
},
|
|
|
|
});
|
2023-07-19 12:22:11 +02:00
|
|
|
});
|
|
|
|
}
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// return true if all selected rows are in "running" state
|
|
|
|
canStopScan(): boolean {
|
|
|
|
if (this.onSendingStopScanCommand) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (this.selectedRow && this.selectedRow.length) {
|
|
|
|
let flag: boolean = true;
|
|
|
|
this.selectedRow.forEach(item => {
|
|
|
|
const st: string = this.scanStatus(item);
|
|
|
|
if (!this.isRunningState(st)) {
|
|
|
|
flag = false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return flag;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
isRunningState(state: string): boolean {
|
2022-05-13 10:00:45 +02:00
|
|
|
return (
|
|
|
|
state === VULNERABILITY_SCAN_STATUS.RUNNING ||
|
2022-01-05 06:41:51 +01:00
|
|
|
state === VULNERABILITY_SCAN_STATUS.PENDING ||
|
2022-05-13 10:00:45 +02:00
|
|
|
state === VULNERABILITY_SCAN_STATUS.SCHEDULED
|
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
stopNow() {
|
|
|
|
if (this.selectedRow && this.selectedRow.length) {
|
|
|
|
this.scanStoppedArtifactLength = 0;
|
|
|
|
this.onStopScanArtifactsLength = this.selectedRow.length;
|
|
|
|
this.onSendingStopScanCommand = true;
|
|
|
|
this.selectedRow.forEach((data: any) => {
|
|
|
|
let digest = data.digest;
|
2022-05-13 10:00:45 +02:00
|
|
|
this.eventService.publish(
|
|
|
|
HarborEvent.STOP_SCAN_ARTIFACT,
|
|
|
|
this.repoName + '/' + digest
|
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2022-01-10 11:14:34 +01:00
|
|
|
tagsString(tags: Tag[]): string {
|
|
|
|
if (tags?.length) {
|
|
|
|
const arr: string[] = [];
|
|
|
|
tags.forEach(item => {
|
2022-05-13 10:00:45 +02:00
|
|
|
arr.push(item.name);
|
2022-01-10 11:14:34 +01:00
|
|
|
});
|
|
|
|
return arr.join(', ');
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
2022-01-10 11:14:34 +01:00
|
|
|
return null;
|
2022-01-05 06:41:51 +01:00
|
|
|
}
|
|
|
|
deleteAccessory(a: Accessory) {
|
2022-05-13 10:00:45 +02:00
|
|
|
let titleKey: string,
|
|
|
|
summaryKey: string,
|
|
|
|
content: string,
|
|
|
|
buttons: ConfirmationButtons;
|
|
|
|
titleKey = 'ACCESSORY.DELETION_TITLE_ACCESSORY';
|
|
|
|
summaryKey = 'ACCESSORY.DELETION_SUMMARY_ONE_ACCESSORY';
|
2022-01-05 06:41:51 +01:00
|
|
|
buttons = ConfirmationButtons.DELETE_CANCEL;
|
|
|
|
content = a.digest.slice(0, 15);
|
|
|
|
let message = new ConfirmationMessage(
|
|
|
|
titleKey,
|
|
|
|
summaryKey,
|
|
|
|
content,
|
|
|
|
a,
|
|
|
|
ConfirmationTargets.ACCESSORY,
|
2022-05-13 10:00:45 +02:00
|
|
|
buttons
|
|
|
|
);
|
2022-01-05 06:41:51 +01:00
|
|
|
this.confirmationDialog.open(message);
|
|
|
|
}
|
2022-05-10 11:47:09 +02:00
|
|
|
isEllipsisActive(ele: HTMLSpanElement): boolean {
|
|
|
|
return ele?.offsetWidth < ele?.scrollWidth;
|
|
|
|
}
|
2017-07-04 12:03:38 +02:00
|
|
|
}
|