mirror of https://github.com/goharbor/harbor.git
199 lines
6.3 KiB
TypeScript
199 lines
6.3 KiB
TypeScript
import {
|
|
Component,
|
|
Input,
|
|
OnInit,
|
|
OnDestroy,
|
|
ChangeDetectorRef,
|
|
} from '@angular/core';
|
|
import { Subscription , timer} from "rxjs";
|
|
|
|
import { clone, DEFAULT_SUPPORTED_MIME_TYPE, VULNERABILITY_SCAN_STATUS } from '../utils';
|
|
import {
|
|
VulnerabilitySummary,
|
|
TagService,
|
|
ScanningResultService,
|
|
Tag
|
|
} from '../service/index';
|
|
import { ErrorHandler } from '../error-handler/index';
|
|
import { ChannelService } from '../channel/index';
|
|
import { JobLogService } from "../service/index";
|
|
|
|
const STATE_CHECK_INTERVAL: number = 3000; // 3s
|
|
const RETRY_TIMES: number = 3;
|
|
|
|
@Component({
|
|
selector: 'hbr-vulnerability-bar',
|
|
templateUrl: './result-bar-chart-component.html',
|
|
styleUrls: ['./scanning.scss']
|
|
})
|
|
export class ResultBarChartComponent implements OnInit, OnDestroy {
|
|
@Input() repoName: string = "";
|
|
@Input() tagId: string = "";
|
|
@Input() summary: VulnerabilitySummary;
|
|
onSubmitting: boolean = false;
|
|
retryCounter: number = 0;
|
|
stateCheckTimer: Subscription;
|
|
scanSubscription: Subscription;
|
|
timerHandler: any;
|
|
|
|
constructor(
|
|
private tagService: TagService,
|
|
private scanningService: ScanningResultService,
|
|
private errorHandler: ErrorHandler,
|
|
private channel: ChannelService,
|
|
private ref: ChangeDetectorRef,
|
|
private jobLogService: JobLogService,
|
|
) { }
|
|
|
|
ngOnInit(): void {
|
|
if ((this.status === VULNERABILITY_SCAN_STATUS.RUNNING ||
|
|
this.status === VULNERABILITY_SCAN_STATUS.PENDING) &&
|
|
!this.stateCheckTimer) {
|
|
// Avoid duplicated subscribing
|
|
this.stateCheckTimer = timer(0, STATE_CHECK_INTERVAL).subscribe(() => {
|
|
this.getSummary();
|
|
});
|
|
}
|
|
this.scanSubscription = this.channel.scanCommand$.subscribe((tagId: string) => {
|
|
let myFullTag: string = this.repoName + "/" + this.tagId;
|
|
if (myFullTag === tagId) {
|
|
this.scanNow();
|
|
}
|
|
});
|
|
}
|
|
|
|
ngOnDestroy(): void {
|
|
if (this.stateCheckTimer) {
|
|
this.stateCheckTimer.unsubscribe();
|
|
this.stateCheckTimer = null;
|
|
}
|
|
if (this.scanSubscription) {
|
|
this.scanSubscription.unsubscribe();
|
|
}
|
|
}
|
|
|
|
// Get vulnerability scanning status
|
|
public get status(): string {
|
|
if (this.summary && this.summary.scan_status) {
|
|
return this.summary.scan_status;
|
|
}
|
|
return VULNERABILITY_SCAN_STATUS.NOT_SCANNED;
|
|
}
|
|
|
|
public get completed(): boolean {
|
|
return this.status === VULNERABILITY_SCAN_STATUS.SUCCESS;
|
|
}
|
|
|
|
public get error(): boolean {
|
|
return this.status === VULNERABILITY_SCAN_STATUS.ERROR;
|
|
}
|
|
|
|
public get queued(): boolean {
|
|
return this.status === VULNERABILITY_SCAN_STATUS.PENDING;
|
|
}
|
|
|
|
public get scanning(): boolean {
|
|
return this.status === VULNERABILITY_SCAN_STATUS.RUNNING;
|
|
}
|
|
public get otherStatus(): boolean {
|
|
return !(this.completed || this.error || this.queued || this.scanning);
|
|
}
|
|
|
|
scanNow(): void {
|
|
if (this.onSubmitting) {
|
|
// Avoid duplicated submitting
|
|
console.log("duplicated submit");
|
|
return;
|
|
}
|
|
|
|
if (!this.repoName || !this.tagId) {
|
|
console.log("bad repository or tag");
|
|
return;
|
|
}
|
|
|
|
this.onSubmitting = true;
|
|
|
|
this.scanningService.startVulnerabilityScanning(this.repoName, this.tagId)
|
|
.subscribe(() => {
|
|
this.onSubmitting = false;
|
|
|
|
// Forcely change status to queued after successful submitting
|
|
this.summary = {
|
|
scan_status: VULNERABILITY_SCAN_STATUS.PENDING,
|
|
};
|
|
|
|
// Forcely refresh view
|
|
this.forceRefreshView(1000);
|
|
|
|
// Start check status util the job is done
|
|
if (!this.stateCheckTimer) {
|
|
// Avoid duplicated subscribing
|
|
this.stateCheckTimer = timer(STATE_CHECK_INTERVAL, STATE_CHECK_INTERVAL).subscribe(() => {
|
|
this.getSummary();
|
|
});
|
|
}
|
|
}, error => {
|
|
this.onSubmitting = false;
|
|
this.errorHandler.error(error);
|
|
});
|
|
}
|
|
|
|
getSummary(): void {
|
|
if (!this.repoName || !this.tagId) {
|
|
return;
|
|
}
|
|
|
|
this.tagService.getTag(this.repoName, this.tagId)
|
|
.subscribe((t: Tag) => {
|
|
// To keep the same summary reference, use value copy.
|
|
if (t.scan_overview) {
|
|
this.copyValue(t.scan_overview[DEFAULT_SUPPORTED_MIME_TYPE]);
|
|
}
|
|
// Forcely refresh view
|
|
this.forceRefreshView(1000);
|
|
|
|
if (!this.queued && !this.scanning) {
|
|
// Scanning should be done
|
|
if (this.stateCheckTimer) {
|
|
this.stateCheckTimer.unsubscribe();
|
|
this.stateCheckTimer = null;
|
|
}
|
|
}
|
|
this.channel.tagDetail$.next(t);
|
|
}, error => {
|
|
this.errorHandler.error(error);
|
|
this.retryCounter++;
|
|
if (this.retryCounter >= RETRY_TIMES) {
|
|
// Stop timer
|
|
if (this.stateCheckTimer) {
|
|
this.stateCheckTimer.unsubscribe();
|
|
this.stateCheckTimer = null;
|
|
}
|
|
this.retryCounter = 0;
|
|
}
|
|
});
|
|
}
|
|
|
|
copyValue(newVal: VulnerabilitySummary): void {
|
|
if (!this.summary || !newVal || !newVal.scan_status) { return; }
|
|
this.summary = clone(newVal);
|
|
}
|
|
|
|
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);
|
|
}
|
|
viewLog(): string {
|
|
return `/api/repositories/${this.repoName}/tags/${this.tagId}/scan/${this.summary.report_id}/log`;
|
|
}
|
|
}
|