mirror of https://github.com/goharbor/harbor.git
436 lines
16 KiB
TypeScript
436 lines
16 KiB
TypeScript
import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
|
import { NewRobotComponent } from './new-robot/new-robot.component';
|
|
import { ViewTokenComponent } from '../../../shared/components/view-token/view-token.component';
|
|
import { RobotService } from '../../../../../ng-swagger-gen/services/robot.service';
|
|
import { Robot } from '../../../../../ng-swagger-gen/models/robot';
|
|
import {
|
|
clone,
|
|
getPageSizeFromLocalStorage,
|
|
getSortingString,
|
|
PageSizeMapKeys,
|
|
setPageSizeToLocalStorage,
|
|
} from '../../../shared/units/utils';
|
|
import { ClrDatagridStateInterface, ClrLoadingState } from '@clr/angular';
|
|
import {
|
|
catchError,
|
|
debounceTime,
|
|
distinctUntilChanged,
|
|
finalize,
|
|
map,
|
|
switchMap,
|
|
} from 'rxjs/operators';
|
|
import { MessageHandlerService } from '../../../shared/services/message-handler.service';
|
|
import {
|
|
FrontRobot,
|
|
getSystemAccess,
|
|
NAMESPACE_ALL_PROJECTS,
|
|
NEW_EMPTY_ROBOT,
|
|
PermissionsKinds,
|
|
} from './system-robot-util';
|
|
import { ProjectsModalComponent } from './projects-modal/projects-modal.component';
|
|
import { forkJoin, Observable, of, Subscription } from 'rxjs';
|
|
import { FilterComponent } from '../../../shared/components/filter/filter.component';
|
|
import { HttpErrorResponse } from '@angular/common/http';
|
|
import {
|
|
operateChanges,
|
|
OperateInfo,
|
|
OperationState,
|
|
} from '../../../shared/components/operation/operate';
|
|
import { OperationService } from '../../../shared/components/operation/operation.service';
|
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
import { TranslateService } from '@ngx-translate/core';
|
|
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 { RobotPermission } from '../../../../../ng-swagger-gen/models/robot-permission';
|
|
import { SysteminfoService } from '../../../../../ng-swagger-gen/services/systeminfo.service';
|
|
import { Access } from '../../../../../ng-swagger-gen/models/access';
|
|
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: 'system-robot-accounts',
|
|
templateUrl: './system-robot-accounts.component.html',
|
|
styleUrls: ['./system-robot-accounts.component.scss'],
|
|
})
|
|
export class SystemRobotAccountsComponent implements OnInit, OnDestroy {
|
|
pageSize: number = getPageSizeFromLocalStorage(
|
|
PageSizeMapKeys.SYSTEM_ROBOT_COMPONENT
|
|
);
|
|
currentPage: number = 1;
|
|
total: number = 0;
|
|
robots: FrontRobot[] = [];
|
|
selectedRows: FrontRobot[] = [];
|
|
loading: boolean = true;
|
|
loadingData: boolean = false;
|
|
addBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
|
@ViewChild(NewRobotComponent, { static: true })
|
|
newRobotComponent: NewRobotComponent;
|
|
@ViewChild(ViewTokenComponent)
|
|
viewTokenComponent: ViewTokenComponent;
|
|
@ViewChild(ProjectsModalComponent)
|
|
projectsModalComponent: ProjectsModalComponent;
|
|
@ViewChild(FilterComponent, { static: true })
|
|
filterComponent: FilterComponent;
|
|
searchSub: Subscription;
|
|
searchKey: string;
|
|
subscription: Subscription;
|
|
deltaTime: number; // the different between server time and local time
|
|
|
|
robotMetadata: Permissions;
|
|
loadingMetadata: boolean = false;
|
|
constructor(
|
|
private robotService: RobotService,
|
|
private msgHandler: MessageHandlerService,
|
|
private operateDialogService: ConfirmationDialogService,
|
|
private operationService: OperationService,
|
|
private sanitizer: DomSanitizer,
|
|
private translate: TranslateService,
|
|
private systemInfoService: SysteminfoService,
|
|
private permissionService: PermissionsService
|
|
) {}
|
|
ngOnInit() {
|
|
this.getRobotPermissions();
|
|
this.getCurrenTime();
|
|
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(
|
|
`name=~${this.searchKey}`
|
|
);
|
|
}
|
|
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[];
|
|
this.calculateProjects();
|
|
},
|
|
error => {
|
|
this.msgHandler.handleError(error);
|
|
}
|
|
);
|
|
}
|
|
if (!this.subscription) {
|
|
this.subscription =
|
|
this.operateDialogService.confirmationConfirm$.subscribe(
|
|
message => {
|
|
if (
|
|
message &&
|
|
message.state === ConfirmationState.CONFIRMED &&
|
|
message.source === ConfirmationTargets.ROBOT_ACCOUNT
|
|
) {
|
|
this.deleteRobots(message.data);
|
|
}
|
|
if (
|
|
message.state === ConfirmationState.CONFIRMED &&
|
|
message.source ===
|
|
ConfirmationTargets.ROBOT_ACCOUNT_ENABLE_OR_DISABLE
|
|
) {
|
|
this.operateRobot();
|
|
}
|
|
}
|
|
);
|
|
}
|
|
}
|
|
ngOnDestroy() {
|
|
if (this.searchSub) {
|
|
this.searchSub.unsubscribe();
|
|
this.searchSub = null;
|
|
}
|
|
if (this.subscription) {
|
|
this.subscription.unsubscribe();
|
|
this.subscription = null;
|
|
}
|
|
}
|
|
|
|
getRobotPermissions() {
|
|
this.loadingData = true;
|
|
this.permissionService
|
|
.getPermissions()
|
|
.pipe(finalize(() => (this.loadingData = 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();
|
|
}
|
|
});
|
|
}
|
|
|
|
clrLoad(state?: ClrDatagridStateInterface) {
|
|
if (state && state.page && state.page.size) {
|
|
this.pageSize = state.page.size;
|
|
setPageSizeToLocalStorage(
|
|
PageSizeMapKeys.SYSTEM_ROBOT_COMPONENT,
|
|
this.pageSize
|
|
);
|
|
}
|
|
this.selectedRows = [];
|
|
const queryParam: RobotService.ListRobotParams = {
|
|
page: this.currentPage,
|
|
pageSize: this.pageSize,
|
|
sort: getSortingString(state),
|
|
};
|
|
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[];
|
|
this.calculateProjects();
|
|
},
|
|
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]);
|
|
}
|
|
calculateProjects() {
|
|
if (this.robots && this.robots.length) {
|
|
for (let i = 0; i < this.robots.length; i++) {
|
|
if (
|
|
this.robots[i] &&
|
|
this.robots[i].permissions &&
|
|
this.robots[i].permissions.length
|
|
) {
|
|
for (
|
|
let j = 0;
|
|
j < this.robots[i].permissions.length;
|
|
j++
|
|
) {
|
|
if (
|
|
this.robots[i].permissions[j].kind ===
|
|
PermissionsKinds.PROJECT &&
|
|
this.robots[i].permissions[j].namespace ===
|
|
NAMESPACE_ALL_PROJECTS
|
|
) {
|
|
this.robots[i].permissionScope = {
|
|
coverAll: true,
|
|
access: this.robots[i].permissions[j].access,
|
|
};
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
getProjects(r: Robot): RobotPermission[] {
|
|
const arr = [];
|
|
if (r && r.permissions && r.permissions.length) {
|
|
for (let i = 0; i < r.permissions.length; i++) {
|
|
if (r.permissions[i].kind === PermissionsKinds.PROJECT) {
|
|
arr.push(r.permissions[i]);
|
|
}
|
|
}
|
|
}
|
|
return arr;
|
|
}
|
|
openProjectModal(permissions: RobotPermission[], robotName: string) {
|
|
this.projectsModalComponent.projectsModalOpened = true;
|
|
this.projectsModalComponent.robotName = robotName;
|
|
this.projectsModalComponent.permissions = permissions;
|
|
this.projectsModalComponent.clrDgRefresh();
|
|
}
|
|
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.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.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();
|
|
}
|
|
|
|
getSystemAccess(r: Robot): Access[] {
|
|
return getSystemAccess(r);
|
|
}
|
|
|
|
protected readonly NEW_EMPTY_ROBOT = NEW_EMPTY_ROBOT;
|
|
protected readonly PermissionSelectPanelModes = PermissionSelectPanelModes;
|
|
}
|