mirror of https://github.com/goharbor/harbor.git
440 lines
17 KiB
TypeScript
440 lines
17 KiB
TypeScript
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
|
import { ClrDatagridStateInterface, ClrLoadingState } from '@clr/angular';
|
|
import {
|
|
catchError,
|
|
debounceTime,
|
|
distinctUntilChanged,
|
|
finalize,
|
|
map,
|
|
switchMap,
|
|
} from 'rxjs/operators';
|
|
import { forkJoin, Observable, of, Subscription } from 'rxjs';
|
|
import {
|
|
UserPermissionService,
|
|
USERSTATICPERMISSION,
|
|
} from '../../../shared/services';
|
|
import { PermissionsKinds } from '../../left-side-nav/system-robot-accounts/system-robot-util';
|
|
import {
|
|
clone,
|
|
getPageSizeFromLocalStorage,
|
|
getSortingString,
|
|
PageSizeMapKeys,
|
|
setPageSizeToLocalStorage,
|
|
} from '../../../shared/units/utils';
|
|
import { ViewTokenComponent } from '../../../shared/components/view-token/view-token.component';
|
|
import { FilterComponent } from '../../../shared/components/filter/filter.component';
|
|
import { MessageHandlerService } from '../../../shared/services/message-handler.service';
|
|
import { OperationService } from '../../../shared/components/operation/operation.service';
|
|
import { RobotService } from '../../../../../ng-swagger-gen/services/robot.service';
|
|
import { Robot } from '../../../../../ng-swagger-gen/models/robot';
|
|
import { ActivatedRoute } from '@angular/router';
|
|
import { Project } from '../../../../../ng-swagger-gen/models/project';
|
|
import { HttpErrorResponse } from '@angular/common/http';
|
|
import {
|
|
operateChanges,
|
|
OperateInfo,
|
|
OperationState,
|
|
} from '../../../shared/components/operation/operate';
|
|
import { AddRobotComponent } from './add-robot/add-robot.component';
|
|
import { TranslateService } from '@ngx-translate/core';
|
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
import { ConfirmationDialogService } from '../../global-confirmation-dialog/confirmation-dialog.service';
|
|
import {
|
|
ConfirmationButtons,
|
|
ConfirmationState,
|
|
ConfirmationTargets,
|
|
} from '../../../shared/entities/shared.const';
|
|
import { errorHandler } from '../../../shared/units/shared.utils';
|
|
import { ConfirmationMessage } from '../../global-confirmation-dialog/confirmation-message';
|
|
import { SysteminfoService } from '../../../../../ng-swagger-gen/services/systeminfo.service';
|
|
import { PermissionSelectPanelModes } from '../../../shared/components/robot-permissions-panel/robot-permissions-panel.component';
|
|
import { PermissionsService } from '../../../../../ng-swagger-gen/services/permissions.service';
|
|
import { Permissions } from '../../../../../ng-swagger-gen/models/permissions';
|
|
|
|
@Component({
|
|
selector: 'app-robot-account',
|
|
templateUrl: './robot-account.component.html',
|
|
styleUrls: ['./robot-account.component.scss'],
|
|
})
|
|
export class RobotAccountComponent implements OnInit, OnDestroy {
|
|
pageSize: number = getPageSizeFromLocalStorage(
|
|
PageSizeMapKeys.PROJECT_ROBOT_COMPONENT
|
|
);
|
|
currentPage: number = 1;
|
|
total: number = 0;
|
|
robots: Robot[] = [];
|
|
selectedRows: Robot[] = [];
|
|
loading: boolean = true;
|
|
addBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
@ViewChild(AddRobotComponent)
|
|
newRobotComponent: AddRobotComponent;
|
|
@ViewChild(ViewTokenComponent)
|
|
viewTokenComponent: ViewTokenComponent;
|
|
@ViewChild(FilterComponent, { static: true })
|
|
filterComponent: FilterComponent;
|
|
searchSub: Subscription;
|
|
searchKey: string;
|
|
subscription: Subscription;
|
|
hasRobotCreatePermission: boolean;
|
|
hasRobotUpdatePermission: boolean;
|
|
hasRobotDeletePermission: boolean;
|
|
hasRobotReadPermission: boolean;
|
|
projectId: number;
|
|
projectName: string;
|
|
deltaTime: number; // the different between server time and local time
|
|
|
|
loadingMetadata: boolean = false;
|
|
robotMetadata: Permissions;
|
|
constructor(
|
|
private robotService: RobotService,
|
|
private msgHandler: MessageHandlerService,
|
|
private operateDialogService: ConfirmationDialogService,
|
|
private operationService: OperationService,
|
|
private userPermissionService: UserPermissionService,
|
|
private route: ActivatedRoute,
|
|
private translate: TranslateService,
|
|
private sanitizer: DomSanitizer,
|
|
private systemInfoService: SysteminfoService,
|
|
private permissionService: PermissionsService
|
|
) {}
|
|
ngOnInit() {
|
|
this.getCurrenTime();
|
|
this.projectId = +this.route.snapshot.parent.parent.params['id'];
|
|
let resolverData = this.route.snapshot.parent.parent.data;
|
|
if (resolverData) {
|
|
let project = <Project>resolverData['projectResolver'];
|
|
this.projectName = project.name;
|
|
}
|
|
this.getPermissionsList();
|
|
if (!this.searchSub) {
|
|
this.searchSub = this.filterComponent.filterTerms
|
|
.pipe(
|
|
debounceTime(500),
|
|
distinctUntilChanged(),
|
|
switchMap(robotSearchName => {
|
|
this.currentPage = 1;
|
|
this.selectedRows = [];
|
|
const queryParam: RobotService.ListRobotParams = {
|
|
page: this.currentPage,
|
|
pageSize: this.pageSize,
|
|
};
|
|
this.searchKey = robotSearchName;
|
|
if (this.searchKey) {
|
|
queryParam.q = encodeURIComponent(
|
|
`Level=${PermissionsKinds.PROJECT},ProjectID=${this.projectId},name=~${this.searchKey}`
|
|
);
|
|
} else {
|
|
queryParam.q = encodeURIComponent(
|
|
`Level=${PermissionsKinds.PROJECT},ProjectID=${this.projectId}`
|
|
);
|
|
}
|
|
this.loading = true;
|
|
return this.robotService
|
|
.ListRobotResponse(queryParam)
|
|
.pipe(
|
|
finalize(() => {
|
|
this.loading = false;
|
|
})
|
|
);
|
|
})
|
|
)
|
|
.subscribe(
|
|
response => {
|
|
this.total = Number.parseInt(
|
|
response.headers.get('x-total-count'),
|
|
10
|
|
);
|
|
this.robots = response.body as Robot[];
|
|
},
|
|
error => {
|
|
this.msgHandler.handleError(error);
|
|
}
|
|
);
|
|
}
|
|
if (!this.subscription) {
|
|
this.subscription =
|
|
this.operateDialogService.confirmationConfirm$.subscribe(
|
|
message => {
|
|
if (
|
|
message &&
|
|
message.state === ConfirmationState.CONFIRMED &&
|
|
message.source ===
|
|
ConfirmationTargets.PROJECT_ROBOT_ACCOUNT
|
|
) {
|
|
this.deleteRobots(message.data);
|
|
}
|
|
if (
|
|
message.state === ConfirmationState.CONFIRMED &&
|
|
message.source ===
|
|
ConfirmationTargets.PROJECT_ROBOT_ACCOUNT_ENABLE_OR_DISABLE
|
|
) {
|
|
this.operateRobot();
|
|
}
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
getRobotPermissions() {
|
|
this.loadingMetadata = true;
|
|
this.permissionService
|
|
.getPermissions()
|
|
.pipe(finalize(() => (this.loadingMetadata = false)))
|
|
.subscribe(res => {
|
|
this.robotMetadata = res;
|
|
});
|
|
}
|
|
|
|
getCurrenTime() {
|
|
this.systemInfoService.getSystemInfo().subscribe(res => {
|
|
if (res?.current_time) {
|
|
this.deltaTime =
|
|
new Date().getTime() -
|
|
new Date(res?.current_time).getTime();
|
|
}
|
|
});
|
|
}
|
|
getPermissionsList(): void {
|
|
let permissionsList = [];
|
|
permissionsList.push(
|
|
this.userPermissionService.getPermission(
|
|
this.projectId,
|
|
USERSTATICPERMISSION.ROBOT.KEY,
|
|
USERSTATICPERMISSION.ROBOT.VALUE.CREATE
|
|
)
|
|
);
|
|
permissionsList.push(
|
|
this.userPermissionService.getPermission(
|
|
this.projectId,
|
|
USERSTATICPERMISSION.ROBOT.KEY,
|
|
USERSTATICPERMISSION.ROBOT.VALUE.UPDATE
|
|
)
|
|
);
|
|
permissionsList.push(
|
|
this.userPermissionService.getPermission(
|
|
this.projectId,
|
|
USERSTATICPERMISSION.ROBOT.KEY,
|
|
USERSTATICPERMISSION.ROBOT.VALUE.DELETE
|
|
)
|
|
);
|
|
permissionsList.push(
|
|
this.userPermissionService.getPermission(
|
|
this.projectId,
|
|
USERSTATICPERMISSION.ROBOT.KEY,
|
|
USERSTATICPERMISSION.ROBOT.VALUE.READ
|
|
)
|
|
);
|
|
|
|
forkJoin(...permissionsList).subscribe(
|
|
Rules => {
|
|
this.hasRobotCreatePermission = Rules[0] as boolean;
|
|
if (this.hasRobotCreatePermission) {
|
|
this.getRobotPermissions();
|
|
}
|
|
this.hasRobotUpdatePermission = Rules[1] as boolean;
|
|
this.hasRobotDeletePermission = Rules[2] as boolean;
|
|
this.hasRobotReadPermission = Rules[3] as boolean;
|
|
},
|
|
error => this.msgHandler.error(error)
|
|
);
|
|
}
|
|
ngOnDestroy() {
|
|
if (this.searchSub) {
|
|
this.searchSub.unsubscribe();
|
|
this.searchSub = null;
|
|
}
|
|
if (this.subscription) {
|
|
this.subscription.unsubscribe();
|
|
this.subscription = null;
|
|
}
|
|
}
|
|
clrLoad(state?: ClrDatagridStateInterface) {
|
|
if (state && state.page && state.page.size) {
|
|
this.pageSize = state.page.size;
|
|
setPageSizeToLocalStorage(
|
|
PageSizeMapKeys.PROJECT_ROBOT_COMPONENT,
|
|
this.pageSize
|
|
);
|
|
}
|
|
this.selectedRows = [];
|
|
const queryParam: RobotService.ListRobotParams = {
|
|
page: this.currentPage,
|
|
pageSize: this.pageSize,
|
|
sort: getSortingString(state),
|
|
q: encodeURIComponent(
|
|
`Level=${PermissionsKinds.PROJECT},ProjectID=${this.projectId}`
|
|
),
|
|
};
|
|
if (this.searchKey) {
|
|
queryParam.q += encodeURIComponent(`,name=~${this.searchKey}`);
|
|
}
|
|
this.loading = true;
|
|
this.robotService
|
|
.ListRobotResponse(queryParam)
|
|
.pipe(finalize(() => (this.loading = false)))
|
|
.subscribe(
|
|
response => {
|
|
this.total = Number.parseInt(
|
|
response.headers.get('x-total-count'),
|
|
10
|
|
);
|
|
this.robots = response.body as Robot[];
|
|
},
|
|
err => {
|
|
this.msgHandler.error(err);
|
|
}
|
|
);
|
|
}
|
|
openNewRobotModal(isEditMode: boolean) {
|
|
if (isEditMode) {
|
|
this.newRobotComponent.resetForEdit(clone(this.selectedRows[0]));
|
|
} else {
|
|
this.newRobotComponent.reset();
|
|
}
|
|
}
|
|
openTokenModal() {
|
|
this.viewTokenComponent.open();
|
|
this.viewTokenComponent.robot = clone(this.selectedRows[0]);
|
|
}
|
|
refresh() {
|
|
this.currentPage = 1;
|
|
this.selectedRows = [];
|
|
this.clrLoad();
|
|
}
|
|
deleteRobots(robots: Robot[]) {
|
|
let observableLists: Observable<any>[] = [];
|
|
if (robots && robots.length) {
|
|
robots.forEach(item => {
|
|
observableLists.push(this.deleteRobot(item));
|
|
});
|
|
forkJoin(...observableLists).subscribe(resArr => {
|
|
let error;
|
|
if (resArr && resArr.length) {
|
|
resArr.forEach(item => {
|
|
if (item instanceof HttpErrorResponse) {
|
|
error = errorHandler(item);
|
|
}
|
|
});
|
|
}
|
|
if (error) {
|
|
this.msgHandler.handleError(error);
|
|
} else {
|
|
this.msgHandler.showSuccess(
|
|
'SYSTEM_ROBOT.DELETE_ROBOT_SUCCESS'
|
|
);
|
|
}
|
|
this.refresh();
|
|
});
|
|
}
|
|
}
|
|
deleteRobot(robot: Robot): Observable<any> {
|
|
let operMessage = new OperateInfo();
|
|
operMessage.name = 'SYSTEM_ROBOT.DELETE_ROBOT';
|
|
operMessage.data.id = robot.id;
|
|
operMessage.state = OperationState.progressing;
|
|
operMessage.data.name = robot.name;
|
|
this.operationService.publishInfo(operMessage);
|
|
return this.robotService.DeleteRobot({ robotId: robot.id }).pipe(
|
|
map(() => {
|
|
operateChanges(operMessage, OperationState.success);
|
|
}),
|
|
catchError(error => {
|
|
const message = errorHandler(error);
|
|
operateChanges(operMessage, OperationState.failure, message);
|
|
return of(error);
|
|
})
|
|
);
|
|
}
|
|
openDeleteRobotsDialog() {
|
|
const robotNames = this.selectedRows.map(robot => robot.name).join(',');
|
|
const deletionMessage = new ConfirmationMessage(
|
|
'ROBOT_ACCOUNT.DELETION_TITLE',
|
|
'ROBOT_ACCOUNT.DELETION_SUMMARY',
|
|
robotNames,
|
|
this.selectedRows,
|
|
ConfirmationTargets.PROJECT_ROBOT_ACCOUNT,
|
|
ConfirmationButtons.DELETE_CANCEL
|
|
);
|
|
this.operateDialogService.openComfirmDialog(deletionMessage);
|
|
}
|
|
|
|
disableOrEnable() {
|
|
const title: string = this.selectedRows[0].disable
|
|
? 'SYSTEM_ROBOT.ENABLE_TITLE'
|
|
: 'SYSTEM_ROBOT.DISABLE_TITLE';
|
|
const summary: string = this.selectedRows[0].disable
|
|
? 'SYSTEM_ROBOT.ENABLE_SUMMARY'
|
|
: 'SYSTEM_ROBOT.DISABLE_SUMMARY';
|
|
const deletionMessage = new ConfirmationMessage(
|
|
title,
|
|
summary,
|
|
this.selectedRows[0].name,
|
|
this.selectedRows[0],
|
|
ConfirmationTargets.PROJECT_ROBOT_ACCOUNT_ENABLE_OR_DISABLE,
|
|
this.selectedRows[0].disable
|
|
? ConfirmationButtons.ENABLE_CANCEL
|
|
: ConfirmationButtons.DISABLE_CANCEL
|
|
);
|
|
this.operateDialogService.openComfirmDialog(deletionMessage);
|
|
}
|
|
|
|
operateRobot() {
|
|
const robot: Robot = clone(this.selectedRows[0]);
|
|
const successMessage: string = robot.disable
|
|
? 'SYSTEM_ROBOT.ENABLE_ROBOT_SUCCESSFULLY'
|
|
: 'SYSTEM_ROBOT.DISABLE_ROBOT_SUCCESSFULLY';
|
|
robot.disable = !robot.disable;
|
|
delete robot.secret;
|
|
const opeMessage = new OperateInfo();
|
|
opeMessage.name = robot.disable
|
|
? 'SYSTEM_ROBOT.DISABLE_TITLE'
|
|
: 'SYSTEM_ROBOT.ENABLE_TITLE';
|
|
opeMessage.data.id = robot.id;
|
|
opeMessage.state = OperationState.progressing;
|
|
opeMessage.data.name = robot.name;
|
|
this.operationService.publishInfo(opeMessage);
|
|
this.robotService
|
|
.UpdateRobot({
|
|
robot: robot,
|
|
robotId: robot.id,
|
|
})
|
|
.subscribe(
|
|
res => {
|
|
operateChanges(opeMessage, OperationState.success);
|
|
this.msgHandler.showSuccess(successMessage);
|
|
this.refresh();
|
|
},
|
|
error => {
|
|
operateChanges(
|
|
opeMessage,
|
|
OperationState.failure,
|
|
errorHandler(error)
|
|
);
|
|
this.msgHandler.showSuccess(error);
|
|
}
|
|
);
|
|
}
|
|
addSuccess(robot: Robot) {
|
|
if (robot) {
|
|
this.viewTokenComponent.open();
|
|
this.viewTokenComponent.tokenModalOpened = false;
|
|
this.viewTokenComponent.robot = clone(robot);
|
|
this.viewTokenComponent.copyToken = true;
|
|
this.translate
|
|
.get('ROBOT_ACCOUNT.CREATED_SUCCESS', { param: robot.name })
|
|
.subscribe((res: string) => {
|
|
this.viewTokenComponent.createSuccess = res;
|
|
});
|
|
// export to token file
|
|
const downLoadUrl = `data:text/json;charset=utf-8, ${encodeURIComponent(
|
|
JSON.stringify(robot)
|
|
)}`;
|
|
this.viewTokenComponent.downLoadHref =
|
|
this.sanitizer.bypassSecurityTrustUrl(downLoadUrl);
|
|
this.viewTokenComponent.downLoadFileName = `${robot.name}.json`;
|
|
}
|
|
this.refresh();
|
|
}
|
|
protected readonly PermissionSelectPanelModes = PermissionSelectPanelModes;
|
|
}
|