mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-26 02:28:06 +01:00
Merge pull request #6823 from pureshine/robot-account
Support Robot account in Harbor
This commit is contained in:
commit
829bfe031e
@ -12,7 +12,7 @@
|
|||||||
@include text-overflow;
|
@include text-overflow;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin grid-left-top-pos{
|
@mixin grid-right-top-pos{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
right: 35px;
|
right: 35px;
|
||||||
|
@ -12,7 +12,7 @@ $size60:60px;
|
|||||||
.toolbar {
|
.toolbar {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
.rightPos {
|
.rightPos {
|
||||||
@include grid-left-top-pos;
|
@include grid-right-top-pos;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
.filter-divider {
|
.filter-divider {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
.toolbar {
|
.toolbar {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
.rightPos {
|
.rightPos {
|
||||||
@include grid-left-top-pos;
|
@include grid-right-top-pos;
|
||||||
.filter-divider {
|
.filter-divider {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
@import '../mixin';
|
@import '../mixin';
|
||||||
|
|
||||||
.rightPos{
|
.rightPos{
|
||||||
@include grid-left-top-pos;
|
@include grid-right-top-pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
|
@ -33,6 +33,7 @@ export const enum ConfirmationTargets {
|
|||||||
PROJECT,
|
PROJECT,
|
||||||
PROJECT_MEMBER,
|
PROJECT_MEMBER,
|
||||||
USER,
|
USER,
|
||||||
|
ROBOT_ACCOUNT,
|
||||||
POLICY,
|
POLICY,
|
||||||
TOGGLE_CONFIRM,
|
TOGGLE_CONFIRM,
|
||||||
TARGET,
|
TARGET,
|
||||||
|
@ -11,27 +11,24 @@
|
|||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from "@angular/core";
|
||||||
import { CoreModule } from '../core/core.module';
|
import { CoreModule } from "../core/core.module";
|
||||||
import { SharedModule } from '../shared/shared.module';
|
import { SharedModule } from "../shared/shared.module";
|
||||||
|
|
||||||
import { ConfigurationComponent } from './config.component';
|
|
||||||
import { ConfigurationService } from './config.service';
|
|
||||||
import { ConfirmMessageHandler } from './config.msg.utils';
|
|
||||||
import { ConfigurationAuthComponent } from './auth/config-auth.component';
|
|
||||||
import { ConfigurationEmailComponent } from './email/config-email.component';
|
|
||||||
import { GcComponent } from './gc/gc.component';
|
|
||||||
import { GcRepoService } from './gc/gc.service';
|
|
||||||
import { GcApiRepository } from './gc/gc.api.repository';
|
|
||||||
import { GcViewModelFactory } from './gc/gc.viewmodel.factory';
|
|
||||||
import { GcUtility } from './gc/gc.utility';
|
|
||||||
|
|
||||||
|
import { ConfigurationComponent } from "./config.component";
|
||||||
|
import { ConfigurationService } from "./config.service";
|
||||||
|
import { ConfirmMessageHandler } from "./config.msg.utils";
|
||||||
|
import { ConfigurationAuthComponent } from "./auth/config-auth.component";
|
||||||
|
import { ConfigurationEmailComponent } from "./email/config-email.component";
|
||||||
|
import { GcComponent } from "./gc/gc.component";
|
||||||
|
import { GcRepoService } from "./gc/gc.service";
|
||||||
|
import { GcApiRepository } from "./gc/gc.api.repository";
|
||||||
|
import { RobotApiRepository } from "../project/robot-account/robot.api.repository";
|
||||||
|
import { GcViewModelFactory } from "./gc/gc.viewmodel.factory";
|
||||||
|
import { GcUtility } from "./gc/gc.utility";
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [CoreModule, SharedModule],
|
||||||
CoreModule,
|
|
||||||
SharedModule
|
|
||||||
],
|
|
||||||
declarations: [
|
declarations: [
|
||||||
ConfigurationComponent,
|
ConfigurationComponent,
|
||||||
ConfigurationAuthComponent,
|
ConfigurationAuthComponent,
|
||||||
@ -39,6 +36,14 @@ import { GcUtility } from './gc/gc.utility';
|
|||||||
GcComponent
|
GcComponent
|
||||||
],
|
],
|
||||||
exports: [ConfigurationComponent],
|
exports: [ConfigurationComponent],
|
||||||
providers: [ConfigurationService, GcRepoService, GcApiRepository, GcViewModelFactory, GcUtility, ConfirmMessageHandler]
|
providers: [
|
||||||
|
ConfigurationService,
|
||||||
|
GcRepoService,
|
||||||
|
GcApiRepository,
|
||||||
|
GcViewModelFactory,
|
||||||
|
GcUtility,
|
||||||
|
ConfirmMessageHandler,
|
||||||
|
RobotApiRepository
|
||||||
|
]
|
||||||
})
|
})
|
||||||
export class ConfigurationModule { }
|
export class ConfigurationModule {}
|
||||||
|
@ -44,6 +44,7 @@ import { LeavingRepositoryRouteDeactivate } from './shared/route/leaving-reposit
|
|||||||
import { ProjectComponent } from './project/project.component';
|
import { ProjectComponent } from './project/project.component';
|
||||||
import { ProjectDetailComponent } from './project/project-detail/project-detail.component';
|
import { ProjectDetailComponent } from './project/project-detail/project-detail.component';
|
||||||
import { MemberComponent } from './project/member/member.component';
|
import { MemberComponent } from './project/member/member.component';
|
||||||
|
import { RobotAccountComponent } from './project/robot-account/robot-account.component';
|
||||||
import { ProjectLabelComponent } from "./project/project-label/project-label.component";
|
import { ProjectLabelComponent } from "./project/project-label/project-label.component";
|
||||||
import { ProjectConfigComponent } from './project/project-config/project-config.component';
|
import { ProjectConfigComponent } from './project/project-config/project-config.component';
|
||||||
import { ProjectRoutingResolver } from './project/project-routing-resolver.service';
|
import { ProjectRoutingResolver } from './project/project-routing-resolver.service';
|
||||||
@ -178,6 +179,10 @@ const harborRoutes: Routes = [
|
|||||||
{
|
{
|
||||||
path: 'configs',
|
path: 'configs',
|
||||||
component: ProjectConfigComponent
|
component: ProjectConfigComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'robot-account',
|
||||||
|
component: RobotAccountComponent
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -22,6 +22,9 @@
|
|||||||
<li class="nav-item" *ngIf="isSystemAdmin || isMember">
|
<li class="nav-item" *ngIf="isSystemAdmin || isMember">
|
||||||
<a class="nav-link" routerLink="logs" routerLinkActive="active">{{'PROJECT_DETAIL.LOGS' | translate}}</a>
|
<a class="nav-link" routerLink="logs" routerLinkActive="active">{{'PROJECT_DETAIL.LOGS' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" *ngIf="isSProjectAdmin || isSystemAdmin">
|
||||||
|
<a class="nav-link" routerLink="robot-account" routerLinkActive="active">{{'PROJECT_DETAIL.ROBOT_ACCOUNTS' | translate}}</a>
|
||||||
|
</li>
|
||||||
<li class="nav-item" *ngIf="isSessionValid && (isSystemAdmin || isMember)">
|
<li class="nav-item" *ngIf="isSessionValid && (isSystemAdmin || isMember)">
|
||||||
<a class="nav-link" routerLink="configs" routerLinkActive="active">{{'PROJECT_DETAIL.CONFIG' | translate}}</a>
|
<a class="nav-link" routerLink="configs" routerLinkActive="active">{{'PROJECT_DETAIL.CONFIG' | translate}}</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -30,6 +30,7 @@ import { AddGroupComponent } from './member/add-group/add-group.component';
|
|||||||
|
|
||||||
import { ProjectService } from './project.service';
|
import { ProjectService } from './project.service';
|
||||||
import { MemberService } from './member/member.service';
|
import { MemberService } from './member/member.service';
|
||||||
|
import { RobotService } from './robot-account/robot-account.service';
|
||||||
import { ProjectRoutingResolver } from './project-routing-resolver.service';
|
import { ProjectRoutingResolver } from './project-routing-resolver.service';
|
||||||
|
|
||||||
import { TargetExistsValidatorDirective } from '../shared/target-exists-directive';
|
import { TargetExistsValidatorDirective } from '../shared/target-exists-directive';
|
||||||
@ -37,6 +38,8 @@ import { ProjectLabelComponent } from "../project/project-label/project-label.co
|
|||||||
import { ListChartsComponent } from './list-charts/list-charts.component';
|
import { ListChartsComponent } from './list-charts/list-charts.component';
|
||||||
import { ListChartVersionsComponent } from './list-chart-versions/list-chart-versions.component';
|
import { ListChartVersionsComponent } from './list-chart-versions/list-chart-versions.component';
|
||||||
import { ChartDetailComponent } from './chart-detail/chart-detail.component';
|
import { ChartDetailComponent } from './chart-detail/chart-detail.component';
|
||||||
|
import { RobotAccountComponent } from './robot-account/robot-account.component';
|
||||||
|
import { AddRobotComponent } from './robot-account/add-robot/add-robot.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -58,10 +61,12 @@ import { ChartDetailComponent } from './chart-detail/chart-detail.component';
|
|||||||
AddGroupComponent,
|
AddGroupComponent,
|
||||||
ListChartsComponent,
|
ListChartsComponent,
|
||||||
ListChartVersionsComponent,
|
ListChartVersionsComponent,
|
||||||
ChartDetailComponent
|
ChartDetailComponent,
|
||||||
|
RobotAccountComponent,
|
||||||
|
AddRobotComponent
|
||||||
],
|
],
|
||||||
exports: [ProjectComponent, ListProjectComponent],
|
exports: [ProjectComponent, ListProjectComponent],
|
||||||
providers: [ProjectRoutingResolver, ProjectService, MemberService]
|
providers: [ProjectRoutingResolver, ProjectService, MemberService, RobotService]
|
||||||
})
|
})
|
||||||
export class ProjectModule {
|
export class ProjectModule {
|
||||||
|
|
||||||
|
@ -0,0 +1,102 @@
|
|||||||
|
<clr-modal [(clrModalOpen)]="addRobotOpened"
|
||||||
|
[clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="closable">
|
||||||
|
<h3 class="modal-title">{{'ROBOT_ACCOUNT.CREAT_ROBOT_ACCOUNT' | translate}}</h3>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form #robotForm="ngForm">
|
||||||
|
<section class="form-block">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-3
|
||||||
|
form-group-label-override required" for="robot_name">
|
||||||
|
{{'ROBOT_ACCOUNT.NAME' | translate}}
|
||||||
|
</label>
|
||||||
|
<label aria-haspopup="true" role="tooltip" class="tooltip
|
||||||
|
tooltip-validation
|
||||||
|
tooltip-md tooltip-bottom-left" for="robot_name"
|
||||||
|
[class.invalid]="!isRobotNameValid">
|
||||||
|
<input type="text"
|
||||||
|
[(ngModel)]="robot.name"
|
||||||
|
size="30" class="input-width"
|
||||||
|
name="robot_name"
|
||||||
|
id="robot_name"
|
||||||
|
#robotName="ngModel"
|
||||||
|
required
|
||||||
|
pattern='[^" ~#$%]+'
|
||||||
|
maxLengthExt="255"
|
||||||
|
autocomplete="off"
|
||||||
|
(keyup)='handleValidation()'>
|
||||||
|
<span class="tooltip-content">
|
||||||
|
{{ nameTooltipText | translate }}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<span class="spinner spinner-inline" [hidden]="!checkOnGoing"></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-group-label-override">{{'REPLICATION.DESCRIPTION' |
|
||||||
|
translate}}</label>
|
||||||
|
<input type="text" size="255" class="input-width"
|
||||||
|
[(ngModel)]="robot.description"
|
||||||
|
name="robot_desc" id="robot_desc">
|
||||||
|
</div>
|
||||||
|
<div class="form-group clr-form-control rule-width">
|
||||||
|
<clr-checkbox-wrapper>
|
||||||
|
<input type="checkbox" clrCheckbox [checked]="true"
|
||||||
|
[(ngModel)]="robot.access.isPull" name="isPull"
|
||||||
|
id="permission-pull" class="clr-checkbox">
|
||||||
|
<label for="permission-pull" class="clr-control-label">
|
||||||
|
{{'ROBOT_ACCOUNT.PULL_PERMISSION' | translate}}
|
||||||
|
</label>
|
||||||
|
</clr-checkbox-wrapper>
|
||||||
|
<clr-checkbox-wrapper>
|
||||||
|
<input type="checkbox" clrCheckbox [checked]="true"
|
||||||
|
[(ngModel)]="robot.access.isPush" name="isPush"
|
||||||
|
id="permission-push" class="clr-checkbox">
|
||||||
|
<label for="permission-push" class="clr-control-label">
|
||||||
|
{{'ROBOT_ACCOUNT.PUSH_PERMISSION' | translate}}
|
||||||
|
</label>
|
||||||
|
</clr-checkbox-wrapper>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-outline" (click)="onCancel()">{{'BUTTON.CANCEL'
|
||||||
|
| translate}}</button>
|
||||||
|
<button type="button" [disabled]="shouldDisable" class="btn btn-primary"
|
||||||
|
(click)="onSubmit()">{{'BUTTON.SAVE'
|
||||||
|
| translate}}</button>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
||||||
|
<clr-modal [(clrModalOpen)]="copyToken" class="copy-token"
|
||||||
|
[clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="closable">
|
||||||
|
<div class="modal-title">
|
||||||
|
<h3 class="modal-title">
|
||||||
|
<clr-icon class="alert-icon success-icon" shape="check-circle" size="50"></clr-icon>
|
||||||
|
{{ createSuccess | translate}}</h3>
|
||||||
|
<div class="alert alert-info" role="alert">
|
||||||
|
<div class="alert-items">
|
||||||
|
<div class="alert-item static">
|
||||||
|
<div class="alert-icon-wrapper">
|
||||||
|
<clr-icon class="alert-icon" shape="info-circle"></clr-icon>
|
||||||
|
</div>
|
||||||
|
<span class="alert-text">{{'ROBOT_ACCOUNT.ALERT_TEXT' | translate}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<section class="form-block show-info">
|
||||||
|
<div class="form-group robot-name">
|
||||||
|
<label class="form-group-label-override">{{'ROBOT_ACCOUNT.NAME'
|
||||||
|
| translate}}</label>
|
||||||
|
<span>{{robotAccount}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group robot-token">
|
||||||
|
<label class="form-group-label-override">{{'ROBOT_ACCOUNT.TOKEN' |
|
||||||
|
translate}}</label>
|
||||||
|
<hbr-copy-input (onCopySuccess)="onCpSuccess($event)"
|
||||||
|
(onCopyError)="onCpError($event)" inputSize="50" headerTitle=""
|
||||||
|
defaultValue="{{robotToken}}" class="copy-input"></hbr-copy-input>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</clr-modal>
|
@ -0,0 +1,31 @@
|
|||||||
|
.rule-width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-width {
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-token {
|
||||||
|
.success-icon {
|
||||||
|
color: #318700;
|
||||||
|
}
|
||||||
|
.show-info {
|
||||||
|
.robot-name {
|
||||||
|
margin: 30px 0;
|
||||||
|
|
||||||
|
label {
|
||||||
|
margin-right: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.robot-token {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
label {
|
||||||
|
margin-right: 24px;
|
||||||
|
}
|
||||||
|
.copy-input {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AddRobotComponent } from './add-robot.component';
|
||||||
|
|
||||||
|
describe('AddRobotComponent', () => {
|
||||||
|
let component: AddRobotComponent;
|
||||||
|
let fixture: ComponentFixture<AddRobotComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ AddRobotComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AddRobotComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,191 @@
|
|||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
Input,
|
||||||
|
ViewChild,
|
||||||
|
OnDestroy,
|
||||||
|
Output,
|
||||||
|
EventEmitter,
|
||||||
|
ChangeDetectorRef
|
||||||
|
} from "@angular/core";
|
||||||
|
import { Robot } from "../robot";
|
||||||
|
import { NgForm } from "@angular/forms";
|
||||||
|
import { Subject } from "rxjs";
|
||||||
|
import { debounceTime, finalize } from "rxjs/operators";
|
||||||
|
import { RobotService } from "../robot-account.service";
|
||||||
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
|
import { ErrorHandler } from "@harbor/ui";
|
||||||
|
import { MessageHandlerService } from "../../../shared/message-handler/message-handler.service";
|
||||||
|
import { InlineAlertComponent } from "../../../shared/inline-alert/inline-alert.component";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "add-robot",
|
||||||
|
templateUrl: "./add-robot.component.html",
|
||||||
|
styleUrls: ["./add-robot.component.scss"]
|
||||||
|
})
|
||||||
|
export class AddRobotComponent implements OnInit, OnDestroy {
|
||||||
|
addRobotOpened: boolean;
|
||||||
|
copyToken: boolean;
|
||||||
|
robotToken: string;
|
||||||
|
robotAccount: string;
|
||||||
|
isSubmitOnGoing = false;
|
||||||
|
closable: boolean = false;
|
||||||
|
staticBackdrop: boolean = true;
|
||||||
|
isPull: boolean;
|
||||||
|
isPush: boolean;
|
||||||
|
createSuccess: string;
|
||||||
|
isRobotNameValid: boolean = true;
|
||||||
|
checkOnGoing: boolean = false;
|
||||||
|
robot: Robot = new Robot();
|
||||||
|
robotNameChecker: Subject<string> = new Subject<string>();
|
||||||
|
nameTooltipText = "ROBOT_ACCOUNT.ROBOT_NAME";
|
||||||
|
robotForm: NgForm;
|
||||||
|
@Input() projectId: number;
|
||||||
|
@Input() projectName: string;
|
||||||
|
@Output() create = new EventEmitter<boolean>();
|
||||||
|
@ViewChild("robotForm") currentForm: NgForm;
|
||||||
|
@ViewChild("copyAlert") copyAlert: InlineAlertComponent;
|
||||||
|
constructor(
|
||||||
|
private robotService: RobotService,
|
||||||
|
private translate: TranslateService,
|
||||||
|
private errorHandler: ErrorHandler,
|
||||||
|
private cdr: ChangeDetectorRef,
|
||||||
|
private messageHandlerService: MessageHandlerService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.robotNameChecker.pipe(debounceTime(800)).subscribe((name: string) => {
|
||||||
|
let cont = this.currentForm.controls["robot_name"];
|
||||||
|
if (cont) {
|
||||||
|
this.isRobotNameValid = cont.valid;
|
||||||
|
if (this.isRobotNameValid) {
|
||||||
|
this.checkOnGoing = true;
|
||||||
|
this.robotService
|
||||||
|
.listRobotAccount(this.projectId)
|
||||||
|
.pipe(
|
||||||
|
finalize(() => {
|
||||||
|
this.checkOnGoing = false;
|
||||||
|
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
|
||||||
|
setTimeout(() => clearInterval(hnd), 2000);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe(
|
||||||
|
response => {
|
||||||
|
if (response && response.length) {
|
||||||
|
if (
|
||||||
|
response.find(target => {
|
||||||
|
return target.name === "robot$" + cont.value;
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
this.isRobotNameValid = false;
|
||||||
|
this.nameTooltipText = "ROBOT_ACCOUNT.ACCOUNT_EXISTING";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
this.errorHandler.error(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.nameTooltipText = "ROBOT_ACCOUNT.ROBOT_NAME";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
openAddRobotModal(): void {
|
||||||
|
if (this.isSubmitOnGoing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.robot.name = "";
|
||||||
|
this.robot.description = "";
|
||||||
|
this.addRobotOpened = true;
|
||||||
|
this.isRobotNameValid = true;
|
||||||
|
this.robot = new Robot();
|
||||||
|
this.nameTooltipText = "ROBOT_ACCOUNT.ROBOT_NAME";
|
||||||
|
}
|
||||||
|
|
||||||
|
onCancel(): void {
|
||||||
|
this.addRobotOpened = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.robotNameChecker.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit(): void {
|
||||||
|
if (this.isSubmitOnGoing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.isSubmitOnGoing = true;
|
||||||
|
this.robotService
|
||||||
|
.addRobotAccount(
|
||||||
|
this.projectId,
|
||||||
|
this.robot.name,
|
||||||
|
this.robot.description,
|
||||||
|
this.projectName,
|
||||||
|
this.robot.access.isPull,
|
||||||
|
this.robot.access.isPush
|
||||||
|
)
|
||||||
|
.subscribe(
|
||||||
|
response => {
|
||||||
|
this.isSubmitOnGoing = false;
|
||||||
|
this.robotToken = response.Token;
|
||||||
|
this.robotAccount = response.Name;
|
||||||
|
this.copyToken = true;
|
||||||
|
this.create.emit(true);
|
||||||
|
this.translate
|
||||||
|
.get("ROBOT_ACCOUNT.CREATED_SUCCESS", { param: this.robotAccount })
|
||||||
|
.subscribe((res: string) => {
|
||||||
|
this.createSuccess = res;
|
||||||
|
});
|
||||||
|
this.addRobotOpened = false;
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
this.isSubmitOnGoing = false;
|
||||||
|
this.errorHandler.error(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
isValid(): boolean {
|
||||||
|
return (
|
||||||
|
this.currentForm &&
|
||||||
|
this.currentForm.valid &&
|
||||||
|
!this.isSubmitOnGoing &&
|
||||||
|
this.isRobotNameValid &&
|
||||||
|
!this.checkOnGoing
|
||||||
|
);
|
||||||
|
}
|
||||||
|
get shouldDisable(): boolean {
|
||||||
|
if (this.robot && this.robot.access) {
|
||||||
|
return (
|
||||||
|
!this.isValid() ||
|
||||||
|
(!this.robot.access.isPush && !this.robot.access.isPull)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the form validation
|
||||||
|
handleValidation(): void {
|
||||||
|
let cont = this.currentForm.controls["robot_name"];
|
||||||
|
if (cont) {
|
||||||
|
this.robotNameChecker.next(cont.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCpError($event: any): void {
|
||||||
|
if (this.copyAlert) {
|
||||||
|
this.copyAlert.showInlineError("PUSH_IMAGE.COPY_ERROR");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onCpSuccess($event: any): void {
|
||||||
|
this.copyToken = false;
|
||||||
|
this.translate
|
||||||
|
.get("ROBOT_ACCOUNT.COPY_SUCCESS", { param: this.robotAccount })
|
||||||
|
.subscribe((res: string) => {
|
||||||
|
this.messageHandlerService.showSuccess(res);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
<div class="row robot-space">
|
||||||
|
<div>
|
||||||
|
<div class="row flex-items-xs-between rightPos">
|
||||||
|
<div class="flex-xs-middle option-left">
|
||||||
|
</div>
|
||||||
|
<div class="flex-xs-middle option-right">
|
||||||
|
<hbr-filter [withDivider]="true" filterPlaceholder='{{"
|
||||||
|
ROBOT_ACCOUNT.FILTER_PLACEHOLDER" | translate}}'
|
||||||
|
(filterEvt)="doSearch($event)" [currentValue]="searchRobot"></hbr-filter>
|
||||||
|
<span class="refresh-btn" (click)="retrieve()">
|
||||||
|
<clr-icon shape="refresh"></clr-icon>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||||
|
<clr-dg-action-bar>
|
||||||
|
<button class="btn btn-sm btn-secondary"
|
||||||
|
(click)="openAddRobotModal()">
|
||||||
|
<span><clr-icon shape="plus" size="16"></clr-icon> {{'ROBOT_ACCOUNT.NEW_ROBOT_ACCOUNT'
|
||||||
|
| translate }}</span>
|
||||||
|
</button>
|
||||||
|
<clr-dropdown [clrCloseMenuOnItemClick]="false" class="btn btn-sm
|
||||||
|
btn-link"
|
||||||
|
clrDropdownTrigger>
|
||||||
|
<span>{{'MEMBER.ACTION' | translate}}<clr-icon shape="caret
|
||||||
|
down"></clr-icon></span>
|
||||||
|
<clr-dropdown-menu *clrIfOpen>
|
||||||
|
<button clrDropdownItem [disabled]="!(selectedRow.length ==
|
||||||
|
1)" (click)="changeAccountStatus(selectedRow)">
|
||||||
|
<span *ngIf="selectedRow[0] && !selectedRow[0].disabled
|
||||||
|
|| selectedRow.length!==1">{{'ROBOT_ACCOUNT.DISABLE_ACCOUNT'
|
||||||
|
| translate}}</span>
|
||||||
|
<span *ngIf="selectedRow.length == 1 && selectedRow[0]
|
||||||
|
&& selectedRow[0].disabled">{{'ROBOT_ACCOUNT.ENABLE_ACCOUNT'
|
||||||
|
| translate}}</span>
|
||||||
|
</button>
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
<button clrDropdownItem
|
||||||
|
(click)="openDeleteRobotsDialog(selectedRow)"
|
||||||
|
[disabled]="!selectedRow.length">{{'ROBOT_ACCOUNT.DELETE'
|
||||||
|
| translate}}</button>
|
||||||
|
</clr-dropdown-menu>
|
||||||
|
</clr-dropdown>
|
||||||
|
</clr-dg-action-bar>
|
||||||
|
<clr-datagrid [(clrDgSelected)]="selectedRow" [clrDgLoading]="loading">
|
||||||
|
<clr-dg-column>{{'ROBOT_ACCOUNT.NAME' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'ROBOT_ACCOUNT.ENABLED_STATE' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-column>{{'ROBOT_ACCOUNT.DESCRIPTION' | translate}}</clr-dg-column>
|
||||||
|
<clr-dg-row *clrDgItems="let r of robots" [clrDgItem]="r">
|
||||||
|
<clr-dg-cell>{{r.name}}</clr-dg-cell>
|
||||||
|
<clr-dg-cell [ngSwitch]="r.disabled">
|
||||||
|
<clr-icon shape="check-circle" *ngSwitchCase="false"
|
||||||
|
size="20"
|
||||||
|
class="color-green"></clr-icon>
|
||||||
|
<clr-icon shape="times-circle" *ngSwitchCase="true"
|
||||||
|
size="16"
|
||||||
|
class="color-red red-position"></clr-icon>
|
||||||
|
</clr-dg-cell>
|
||||||
|
<clr-dg-cell>{{r.description}}</clr-dg-cell>
|
||||||
|
</clr-dg-row>
|
||||||
|
<clr-dg-footer>
|
||||||
|
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}}
|
||||||
|
-
|
||||||
|
{{pagination.lastItem +1 }} {{'ROBOT_ACCOUNT.OF' |
|
||||||
|
translate}} </span>
|
||||||
|
{{pagination.totalItems }} {{'ROBOT_ACCOUNT.ITEMS' | translate}}
|
||||||
|
<clr-dg-pagination #pagination [clrDgPageSize]="15"></clr-dg-pagination>
|
||||||
|
</clr-dg-footer>
|
||||||
|
</clr-datagrid>
|
||||||
|
</div>
|
||||||
|
<add-robot [projectId]="projectId" [projectName]="projectName"
|
||||||
|
(create)="createAccount($event)"></add-robot>
|
||||||
|
</div>
|
@ -0,0 +1,28 @@
|
|||||||
|
@import "../../../../lib/src/mixin";
|
||||||
|
|
||||||
|
.robot-space {
|
||||||
|
margin-top: 12px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
clr-icon.red-position {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rightPos {
|
||||||
|
@include grid-right-top-pos;
|
||||||
|
|
||||||
|
.option-left {
|
||||||
|
padding-left: 16px;
|
||||||
|
position: relative;
|
||||||
|
top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-right {
|
||||||
|
padding-right: 16px;
|
||||||
|
|
||||||
|
.refresh-btn {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { RobotAccountComponent } from './robot-account.component';
|
||||||
|
|
||||||
|
describe('RobotAccountComponent', () => {
|
||||||
|
let component: RobotAccountComponent;
|
||||||
|
let fixture: ComponentFixture<RobotAccountComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ RobotAccountComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(RobotAccountComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,201 @@
|
|||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
ViewChild,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectorRef
|
||||||
|
} from "@angular/core";
|
||||||
|
import { AddRobotComponent } from "./add-robot/add-robot.component";
|
||||||
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
import { Robot } from "./robot";
|
||||||
|
import { Project } from "./../project";
|
||||||
|
import { finalize, catchError, map } from "rxjs/operators";
|
||||||
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
|
import { Subscription, forkJoin, Observable, throwError } from "rxjs";
|
||||||
|
import { MessageHandlerService } from "../../shared/message-handler/message-handler.service";
|
||||||
|
import { RobotService } from "./robot-account.service";
|
||||||
|
import { ConfirmationMessage } from "../../shared/confirmation-dialog/confirmation-message";
|
||||||
|
import {
|
||||||
|
ConfirmationTargets,
|
||||||
|
ConfirmationState,
|
||||||
|
ConfirmationButtons
|
||||||
|
} from "../../shared/shared.const";
|
||||||
|
import { ConfirmationDialogService } from "../../shared/confirmation-dialog/confirmation-dialog.service";
|
||||||
|
import {
|
||||||
|
operateChanges,
|
||||||
|
OperateInfo,
|
||||||
|
OperationService,
|
||||||
|
OperationState
|
||||||
|
} from "@harbor/ui";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "app-robot-account",
|
||||||
|
templateUrl: "./robot-account.component.html",
|
||||||
|
styleUrls: ["./robot-account.component.scss"]
|
||||||
|
})
|
||||||
|
export class RobotAccountComponent implements OnInit, OnDestroy {
|
||||||
|
@ViewChild(AddRobotComponent)
|
||||||
|
addRobotComponent: AddRobotComponent;
|
||||||
|
selectedRow: Robot[] = [];
|
||||||
|
robotsCopy: Robot[] = [];
|
||||||
|
loading = false;
|
||||||
|
searchRobot: string;
|
||||||
|
projectName: string;
|
||||||
|
timerHandler: any;
|
||||||
|
batchChangeInfos: {};
|
||||||
|
isDisabled: boolean;
|
||||||
|
isDisabledTip: string = "ROBOT_ACCOUNT.DISABLE_ACCOUNT";
|
||||||
|
robots: Robot[];
|
||||||
|
projectId: number;
|
||||||
|
subscription: Subscription;
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private robotService: RobotService,
|
||||||
|
private OperateDialogService: ConfirmationDialogService,
|
||||||
|
private operationService: OperationService,
|
||||||
|
private translate: TranslateService,
|
||||||
|
private ref: ChangeDetectorRef,
|
||||||
|
private messageHandlerService: MessageHandlerService
|
||||||
|
) {
|
||||||
|
this.subscription = OperateDialogService.confirmationConfirm$.subscribe(
|
||||||
|
message => {
|
||||||
|
if (
|
||||||
|
message &&
|
||||||
|
message.state === ConfirmationState.CONFIRMED &&
|
||||||
|
message.source === ConfirmationTargets.ROBOT_ACCOUNT
|
||||||
|
) {
|
||||||
|
this.delRobots(message.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.forceRefreshView(2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.projectId = +this.route.snapshot.parent.params["id"];
|
||||||
|
let resolverData = this.route.snapshot.parent.data;
|
||||||
|
if (resolverData) {
|
||||||
|
let project = <Project>resolverData["projectResolver"];
|
||||||
|
this.projectName = project.name;
|
||||||
|
}
|
||||||
|
this.searchRobot = "";
|
||||||
|
this.retrieve();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (this.subscription) {
|
||||||
|
this.subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
if (this.timerHandler) {
|
||||||
|
clearInterval(this.timerHandler);
|
||||||
|
this.timerHandler = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openAddRobotModal(): void {
|
||||||
|
this.addRobotComponent.openAddRobotModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
openDeleteRobotsDialog(robots: Robot[]) {
|
||||||
|
let robotNames = robots.map(robot => robot.name).join(",");
|
||||||
|
let deletionMessage = new ConfirmationMessage(
|
||||||
|
"ROBOT_ACCOUNT.DELETION_TITLE",
|
||||||
|
"ROBOT_ACCOUNT.DELETION_SUMMARY",
|
||||||
|
robotNames,
|
||||||
|
robots,
|
||||||
|
ConfirmationTargets.ROBOT_ACCOUNT,
|
||||||
|
ConfirmationButtons.DELETE_CANCEL
|
||||||
|
);
|
||||||
|
this.OperateDialogService.openComfirmDialog(deletionMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
delRobots(robots: Robot[]): void {
|
||||||
|
if (robots && robots.length < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let robotsDelete$ = robots.map(robot => this.delOperate(robot));
|
||||||
|
forkJoin(robotsDelete$)
|
||||||
|
.pipe(
|
||||||
|
catchError(err => throwError(err)),
|
||||||
|
finalize(() => {
|
||||||
|
this.retrieve();
|
||||||
|
this.selectedRow = [];
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe(() => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
delOperate(robot: Robot) {
|
||||||
|
// init operation info
|
||||||
|
let operMessage = new OperateInfo();
|
||||||
|
operMessage.name = "OPERATION.DELETE_ROBOT";
|
||||||
|
operMessage.data.id = robot.id;
|
||||||
|
operMessage.state = OperationState.progressing;
|
||||||
|
operMessage.data.name = robot.name;
|
||||||
|
this.operationService.publishInfo(operMessage);
|
||||||
|
|
||||||
|
return this.robotService
|
||||||
|
.deleteRobotAccount(this.projectId, robot.id)
|
||||||
|
.pipe(
|
||||||
|
map(
|
||||||
|
() => operateChanges(operMessage, OperationState.success),
|
||||||
|
err => operateChanges(operMessage, OperationState.failure, err)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
createAccount(created: boolean): void {
|
||||||
|
if (created) {
|
||||||
|
this.retrieve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
doSearch(value: string): void {
|
||||||
|
this.searchRobot = value;
|
||||||
|
this.retrieve();
|
||||||
|
}
|
||||||
|
|
||||||
|
retrieve(): void {
|
||||||
|
this.loading = true;
|
||||||
|
this.selectedRow = [];
|
||||||
|
this.robotService
|
||||||
|
.listRobotAccount(this.projectId)
|
||||||
|
.pipe(finalize(() => (this.loading = false)))
|
||||||
|
.subscribe(
|
||||||
|
response => {
|
||||||
|
this.robots = response.filter(x =>
|
||||||
|
x.name.split('$')[1].includes(this.searchRobot)
|
||||||
|
);
|
||||||
|
this.robotsCopy = response.map(x => Object.assign({}, x));
|
||||||
|
this.forceRefreshView(2000);
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
this.messageHandlerService.handleError(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
changeAccountStatus(robots: Robot): void {
|
||||||
|
let id: number | string = robots[0].id;
|
||||||
|
this.isDisabled = robots[0].disabled ? false : true;
|
||||||
|
this.robotService
|
||||||
|
.toggleDisabledAccount(this.projectId, id, this.isDisabled)
|
||||||
|
.subscribe(response => {
|
||||||
|
this.retrieve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
import { throwError as observableThrowError, Observable } from "rxjs";
|
||||||
|
|
||||||
|
import { map, catchError } from "rxjs/operators";
|
||||||
|
// 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 { Injectable } from "@angular/core";
|
||||||
|
import { Http } from "@angular/http";
|
||||||
|
import { RobotApiRepository } from "./robot.api.repository";
|
||||||
|
@Injectable()
|
||||||
|
export class RobotService {
|
||||||
|
constructor(
|
||||||
|
private http: Http,
|
||||||
|
private robotApiRepository: RobotApiRepository
|
||||||
|
) {}
|
||||||
|
public addRobotAccount(projecId, name, description, projectName, isPull, isPush): Observable<any> {
|
||||||
|
let access = [];
|
||||||
|
if ( isPull ) {
|
||||||
|
access.push({"resource": "/project/" + projecId + "/repository", "action": "pull"});
|
||||||
|
access.push({"resource": "/project/" + projectName + "/repository", "action": "pull"});
|
||||||
|
}
|
||||||
|
if ( isPush ) {
|
||||||
|
access.push({"resource": "/project/" + projecId + "/repository", "action": "push"});
|
||||||
|
access.push({"resource": "/project/" + projectName + "/repository", "action": "push"});
|
||||||
|
}
|
||||||
|
|
||||||
|
let param = {
|
||||||
|
name: name,
|
||||||
|
description: description,
|
||||||
|
access: access
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.robotApiRepository.postRobot(projecId, param);
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteRobotAccount(projecId, id): Observable<any> {
|
||||||
|
return this.robotApiRepository.deleteRobot(projecId, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public listRobotAccount(projecId): Observable<any> {
|
||||||
|
return this.robotApiRepository.listRobot(projecId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getRobotAccount(projecId, id): Observable<any> {
|
||||||
|
return this.robotApiRepository.getRobot(projecId, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public toggleDisabledAccount(projecId, id, isDisabled): Observable<any> {
|
||||||
|
let data = {
|
||||||
|
Disabled: isDisabled
|
||||||
|
};
|
||||||
|
return this.robotApiRepository.toggleDisabledAccount(projecId, id, data);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
import { Injectable } from "@angular/core";
|
||||||
|
import { Http } from "@angular/http";
|
||||||
|
import { throwError as observableThrowError, Observable, pipe } from "rxjs";
|
||||||
|
import { catchError, map } from "rxjs/operators";
|
||||||
|
import { Robot } from './robot';
|
||||||
|
import { HTTP_JSON_OPTIONS } from "../../shared/shared.utils";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RobotApiRepository {
|
||||||
|
constructor(private http: Http) {}
|
||||||
|
|
||||||
|
public postRobot(projectId, param): Observable<any> {
|
||||||
|
return this.http
|
||||||
|
.post(`/api/projects/${projectId}/robots`, param)
|
||||||
|
.pipe(map(response => response.json()))
|
||||||
|
.pipe(catchError(error => observableThrowError(error)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public deleteRobot(projectId, id): Observable<any> {
|
||||||
|
return this.http
|
||||||
|
.delete(`/api/projects/${projectId}/robots/${id}`)
|
||||||
|
.pipe(catchError(error => observableThrowError(error)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public listRobot(projectId): Observable<Robot[]> {
|
||||||
|
return this.http
|
||||||
|
.get(`/api/projects/${projectId}/robots`)
|
||||||
|
.pipe(map(response => response.json() as Robot[]))
|
||||||
|
.pipe(catchError(error => observableThrowError(error)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public getRobot(projectId, id): Observable<Robot[]> {
|
||||||
|
return this.http
|
||||||
|
.get(`/api/projects/${projectId}/robots/${id}`)
|
||||||
|
.pipe(map(response => response.json() as Robot[]))
|
||||||
|
.pipe(catchError(error => observableThrowError(error)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public toggleDisabledAccount(projectId, id, data): Observable<any> {
|
||||||
|
return this.http
|
||||||
|
.put(`/api/projects/${projectId}/robots/${id}`, data)
|
||||||
|
.pipe(catchError(error => observableThrowError(error)));
|
||||||
|
}
|
||||||
|
}
|
20
src/portal/src/app/project/robot-account/robot.ts
Normal file
20
src/portal/src/app/project/robot-account/robot.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
export class Robot {
|
||||||
|
project_id: number;
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
disabled: boolean;
|
||||||
|
access: {
|
||||||
|
isPull: boolean;
|
||||||
|
isPush: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
this.access = <any>{};
|
||||||
|
// this.access[0].action = true;
|
||||||
|
this.access.isPull = true;
|
||||||
|
this.access.isPush = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,7 @@
|
|||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
.batchInfoUl{
|
.batchInfoUl{
|
||||||
padding: 20px; list-style-type: none;
|
padding: 20px; list-style-type: none;
|
||||||
|
@ -35,6 +35,7 @@ export const enum ConfirmationTargets {
|
|||||||
EMPTY,
|
EMPTY,
|
||||||
PROJECT,
|
PROJECT,
|
||||||
PROJECT_MEMBER,
|
PROJECT_MEMBER,
|
||||||
|
ROBOT_ACCOUNT,
|
||||||
USER,
|
USER,
|
||||||
POLICY,
|
POLICY,
|
||||||
TOGGLE_CONFIRM,
|
TOGGLE_CONFIRM,
|
||||||
|
@ -194,7 +194,8 @@
|
|||||||
"LABELS": "Labels",
|
"LABELS": "Labels",
|
||||||
"PROJECTS": "Projects",
|
"PROJECTS": "Projects",
|
||||||
"CONFIG": "Configuration",
|
"CONFIG": "Configuration",
|
||||||
"HELMCHART": "Helm Charts"
|
"HELMCHART": "Helm Charts",
|
||||||
|
"ROBOT_ACCOUNTS": "Robot Accounts"
|
||||||
},
|
},
|
||||||
"PROJECT_CONFIG": {
|
"PROJECT_CONFIG": {
|
||||||
"REGISTRY": "Project registry",
|
"REGISTRY": "Project registry",
|
||||||
@ -258,6 +259,31 @@
|
|||||||
"SET_ROLE": "SET ROLE",
|
"SET_ROLE": "SET ROLE",
|
||||||
"REMOVE": "Remove"
|
"REMOVE": "Remove"
|
||||||
},
|
},
|
||||||
|
"ROBOT_ACCOUNT": {
|
||||||
|
"NAME": "Name",
|
||||||
|
"TOKEN": "Token",
|
||||||
|
"NEW_ROBOT_ACCOUNT": "NEW ROBOT ACCOUNT",
|
||||||
|
"ENABLED_STATE": "Enabled state",
|
||||||
|
"DESCRIPTION": "Description",
|
||||||
|
"ACTION": "Action",
|
||||||
|
"EDIT": "Edit",
|
||||||
|
"ITEMS": "items",
|
||||||
|
"OF": "of",
|
||||||
|
"DISABLE_ACCOUNT": "Disable Account",
|
||||||
|
"ENABLE_ACCOUNT": "Enable Account",
|
||||||
|
"DELETE": "Delete",
|
||||||
|
"CREAT_ROBOT_ACCOUNT": "Creat Robot Account",
|
||||||
|
"PULL_PERMISSION": "Permission for Pull",
|
||||||
|
"PUSH_PERMISSION": "Permission for Push",
|
||||||
|
"FILTER_PLACEHOLDER": "Filter Robot Accounts",
|
||||||
|
"ROBOT_NAME": "Cannot contain special characters(~#$%) and maximum length should be 255 characters.",
|
||||||
|
"ACCOUNT_EXISTING": "Robot Account is already exists.",
|
||||||
|
"ALERT_TEXT": "This is the only time to copy your personal access token.You wont't have another opportunity",
|
||||||
|
"CREATED_SUCCESS": "Created '{{param}}' successfully.",
|
||||||
|
"COPY_SUCCESS": "Copy token successfully of '{{param}}'",
|
||||||
|
"DELETION_TITLE": "Confirm removal of robot accounts",
|
||||||
|
"DELETION_SUMMARY": "Do you want to delete robot accounts {{param}}?"
|
||||||
|
},
|
||||||
"GROUP": {
|
"GROUP": {
|
||||||
"GROUP": "Group",
|
"GROUP": "Group",
|
||||||
"GROUPS": "Groups",
|
"GROUPS": "Groups",
|
||||||
@ -802,6 +828,7 @@
|
|||||||
"DELETE_REPO": "Delete repository",
|
"DELETE_REPO": "Delete repository",
|
||||||
"DELETE_TAG": "Delete tag",
|
"DELETE_TAG": "Delete tag",
|
||||||
"DELETE_USER": "Delete user",
|
"DELETE_USER": "Delete user",
|
||||||
|
"DELETE_ROBOT": "Delete robot",
|
||||||
"DELETE_REGISTRY": "Delete registry",
|
"DELETE_REGISTRY": "Delete registry",
|
||||||
"DELETE_REPLICATION": "Delete replication",
|
"DELETE_REPLICATION": "Delete replication",
|
||||||
"DELETE_MEMBER": "Delete user member",
|
"DELETE_MEMBER": "Delete user member",
|
||||||
|
@ -194,7 +194,8 @@
|
|||||||
"LABELS": "Labels",
|
"LABELS": "Labels",
|
||||||
"PROJECTS": "Proyectos",
|
"PROJECTS": "Proyectos",
|
||||||
"CONFIG": "Configuración",
|
"CONFIG": "Configuración",
|
||||||
"HELMCHART": "Helm Charts"
|
"HELMCHART": "Helm Charts",
|
||||||
|
"ROBOT_ACCOUNTS": "Robot Accounts"
|
||||||
},
|
},
|
||||||
"PROJECT_CONFIG": {
|
"PROJECT_CONFIG": {
|
||||||
"REGISTRY": "Registro de proyectos",
|
"REGISTRY": "Registro de proyectos",
|
||||||
@ -258,6 +259,31 @@
|
|||||||
"SET_ROLE": "SET ROLE",
|
"SET_ROLE": "SET ROLE",
|
||||||
"REMOVE": "Remove"
|
"REMOVE": "Remove"
|
||||||
},
|
},
|
||||||
|
"ROBOT_ACCOUNT": {
|
||||||
|
"NAME": "Name",
|
||||||
|
"TOKEN": "Token",
|
||||||
|
"NEW_ROBOT_ACCOUNT": "NEW ROBOT ACCOUNT",
|
||||||
|
"ENABLED_STATE": "Enabled state",
|
||||||
|
"DESCRIPTION": "Description",
|
||||||
|
"ACTION": "Action",
|
||||||
|
"EDIT": "Edit",
|
||||||
|
"ITEMS": "items",
|
||||||
|
"OF": "of",
|
||||||
|
"DISABLE_ACCOUNT": "Disable Account",
|
||||||
|
"ENABLE_ACCOUNT": "Enable Account",
|
||||||
|
"DELETE": "Delete",
|
||||||
|
"CREAT_ROBOT_ACCOUNT": "Creat Robot Account",
|
||||||
|
"PULL_PERMISSION": "Permission for Pull",
|
||||||
|
"PUSH_PERMISSION": "Permission for Push",
|
||||||
|
"FILTER_PLACEHOLDER": "Filter Robot Accounts",
|
||||||
|
"ROBOT_NAME": "Cannot contain special characters(~#$%) and maximum length should be 255 characters.",
|
||||||
|
"ACCOUNT_EXISTING": "Robot Account is already exists.",
|
||||||
|
"ALERT_TEXT": "This is the only time to copy your personal access token.You wont't have another opportunity",
|
||||||
|
"CREATED_SUCCESS": "Created '{{param}}' successfully.",
|
||||||
|
"COPY_SUCCESS": "Copy token successfully of '{{param}}'",
|
||||||
|
"DELETION_TITLE": "Confirm removal of robot accounts",
|
||||||
|
"DELETION_SUMMARY": "Do you want to delete robot accounts {{param}}?"
|
||||||
|
},
|
||||||
"GROUP": {
|
"GROUP": {
|
||||||
"GROUP": "Group",
|
"GROUP": "Group",
|
||||||
"GROUPS": "Groups",
|
"GROUPS": "Groups",
|
||||||
@ -802,6 +828,7 @@
|
|||||||
"DELETE_REPO": "Delete repository",
|
"DELETE_REPO": "Delete repository",
|
||||||
"DELETE_TAG": "Delete tag",
|
"DELETE_TAG": "Delete tag",
|
||||||
"DELETE_USER": "Delete user",
|
"DELETE_USER": "Delete user",
|
||||||
|
"DELETE_ROBOT": "Delete robot",
|
||||||
"DELETE_REGISTRY": "Delete registry",
|
"DELETE_REGISTRY": "Delete registry",
|
||||||
"DELETE_REPLICATION": "Delete replication",
|
"DELETE_REPLICATION": "Delete replication",
|
||||||
"DELETE_MEMBER": "Delete user member",
|
"DELETE_MEMBER": "Delete user member",
|
||||||
|
@ -180,7 +180,8 @@
|
|||||||
"LABELS": "Labels",
|
"LABELS": "Labels",
|
||||||
"PROJECTS": "Projets",
|
"PROJECTS": "Projets",
|
||||||
"CONFIG": "Configuration",
|
"CONFIG": "Configuration",
|
||||||
"HELMCHART": "Helm Charts"
|
"HELMCHART": "Helm Charts",
|
||||||
|
"ROBOT_ACCOUNTS": "Robot Accounts"
|
||||||
},
|
},
|
||||||
"PROJECT_CONFIG": {
|
"PROJECT_CONFIG": {
|
||||||
"REGISTRY": "Dépôt du Projet",
|
"REGISTRY": "Dépôt du Projet",
|
||||||
@ -242,6 +243,31 @@
|
|||||||
"SET_ROLE": "SET ROLE",
|
"SET_ROLE": "SET ROLE",
|
||||||
"REMOVE": "Remove"
|
"REMOVE": "Remove"
|
||||||
},
|
},
|
||||||
|
"ROBOT_ACCOUNT": {
|
||||||
|
"NAME": "Nom",
|
||||||
|
"TOKEN": "gage ",
|
||||||
|
"NEW_ROBOT_ACCOUNT": "nouveau robot compte ",
|
||||||
|
"ENABLED_STATE": "état d 'activation",
|
||||||
|
"DESCRIPTION": "Description",
|
||||||
|
"ACTION": "Action",
|
||||||
|
"EDIT": "Edit",
|
||||||
|
"ITEMS": "items",
|
||||||
|
"OF": "of",
|
||||||
|
"DISABLE_ACCOUNT": "désactiver le compte ",
|
||||||
|
"ENABLE_ACCOUNT": "permettre à compte ",
|
||||||
|
"DELETE": "Supprimer",
|
||||||
|
"CREAT_ROBOT_ACCOUNT": "créat robot compte ",
|
||||||
|
"PULL_PERMISSION": "Permission for Pull",
|
||||||
|
"PUSH_PERMISSION": "Permission for Push",
|
||||||
|
"FILTER_PLACEHOLDER": "Filter Robot Accounts",
|
||||||
|
"ROBOT_NAME": "ne peut pas contenir de caractères spéciaux(~#$%) et la longueur maximale devrait être de 255 caractères.",
|
||||||
|
"ACCOUNT_EXISTING": "le robot est existe déjà.",
|
||||||
|
"ALERT_TEXT": "This is the only time to copy your personal access token.You wont't have another opportunity",
|
||||||
|
"CREATED_SUCCESS": "Created '{{param}}' successfully.",
|
||||||
|
"COPY_SUCCESS": "Copy token successfully of '{{param}}'",
|
||||||
|
"DELETION_TITLE": "confirmer l'enlèvement des comptes du robot ",
|
||||||
|
"DELETION_SUMMARY": "Voulez-vous supprimer la règle {{param}}?"
|
||||||
|
},
|
||||||
"GROUP": {
|
"GROUP": {
|
||||||
"Group": "Group",
|
"Group": "Group",
|
||||||
"GROUPS": "Groups",
|
"GROUPS": "Groups",
|
||||||
@ -765,6 +791,7 @@
|
|||||||
"DELETE_REPO": "Delete repository",
|
"DELETE_REPO": "Delete repository",
|
||||||
"DELETE_TAG": "Delete tag",
|
"DELETE_TAG": "Delete tag",
|
||||||
"DELETE_USER": "Delete user",
|
"DELETE_USER": "Delete user",
|
||||||
|
"DELETE_ROBOT": "Delete robot",
|
||||||
"DELETE_REGISTRY": "Delete registry",
|
"DELETE_REGISTRY": "Delete registry",
|
||||||
"DELETE_REPLICATION": "Delete replication",
|
"DELETE_REPLICATION": "Delete replication",
|
||||||
"DELETE_MEMBER": "Delete member",
|
"DELETE_MEMBER": "Delete member",
|
||||||
|
@ -192,7 +192,8 @@
|
|||||||
"LABELS": "Etiquetas",
|
"LABELS": "Etiquetas",
|
||||||
"PROJECTS": "Projetos",
|
"PROJECTS": "Projetos",
|
||||||
"CONFIG": "Configuração",
|
"CONFIG": "Configuração",
|
||||||
"HELMCHART": "Helm Charts"
|
"HELMCHART": "Helm Charts",
|
||||||
|
"ROBOT_ACCOUNTS": "Robot Accounts"
|
||||||
},
|
},
|
||||||
"PROJECT_CONFIG": {
|
"PROJECT_CONFIG": {
|
||||||
"REGISTRY": "Registro do Projeto",
|
"REGISTRY": "Registro do Projeto",
|
||||||
@ -256,6 +257,31 @@
|
|||||||
"SET_ROLE": "DEFINIR FUNÇÃO",
|
"SET_ROLE": "DEFINIR FUNÇÃO",
|
||||||
"REMOVE": "Remover"
|
"REMOVE": "Remover"
|
||||||
},
|
},
|
||||||
|
"ROBOT_ACCOUNT": {
|
||||||
|
"NAME": "Nome",
|
||||||
|
"TOKEN": "Token",
|
||||||
|
"NEW_ROBOT_ACCOUNT": "Novo robô conta",
|
||||||
|
"ENABLED_STATE": "Enabled state",
|
||||||
|
"DESCRIPTION": "Descrição",
|
||||||
|
"ACTION": "AÇÃO",
|
||||||
|
"EDIT": "Editar",
|
||||||
|
"ITEMS": "itens",
|
||||||
|
"OF": "de",
|
||||||
|
"DISABLE_ACCOUNT": "Desactivar a conta",
|
||||||
|
"ENABLE_ACCOUNT": "Ativar conta",
|
||||||
|
"DELETE": "Remover",
|
||||||
|
"CREAT_ROBOT_ACCOUNT": "CRIA robô conta",
|
||||||
|
"PULL_PERMISSION": "Permission for Pull",
|
||||||
|
"PUSH_PERMISSION": "Permission for Push",
|
||||||
|
"FILTER_PLACEHOLDER": "Filtro robot accounts",
|
||||||
|
"ROBOT_NAME": "Não Pode conter caracteres especiais(~#$%) e comprimento máximo deveria ser 255 caracteres.",
|
||||||
|
"ACCOUNT_EXISTING": "Robô conta já existe.",
|
||||||
|
"ALERT_TEXT": "É só copiar o token de acesso Pessoal não VAI ter outra oportunidade.",
|
||||||
|
"CREATED_SUCCESS": "Created '{{param}}' successfully.",
|
||||||
|
"COPY_SUCCESS": "Copy token successfully of '{{param}}'",
|
||||||
|
"DELETION_TITLE": "Confirmar a remoção do robô Contas",
|
||||||
|
"DELETION_SUMMARY": "Você quer remover a regra {{param}}?"
|
||||||
|
},
|
||||||
"GROUP": {
|
"GROUP": {
|
||||||
"GROUP": "Grupo",
|
"GROUP": "Grupo",
|
||||||
"GROUPS": "Grupos",
|
"GROUPS": "Grupos",
|
||||||
@ -792,6 +818,7 @@
|
|||||||
"DELETE_REPO": "Remover repositório",
|
"DELETE_REPO": "Remover repositório",
|
||||||
"DELETE_TAG": "Remover tag",
|
"DELETE_TAG": "Remover tag",
|
||||||
"DELETE_USER": "Remover usuário",
|
"DELETE_USER": "Remover usuário",
|
||||||
|
"DELETE_ROBOT": "Delete robot",
|
||||||
"DELETE_REGISTRY": "Remover registry",
|
"DELETE_REGISTRY": "Remover registry",
|
||||||
"DELETE_REPLICATION": "Remover replicação",
|
"DELETE_REPLICATION": "Remover replicação",
|
||||||
"DELETE_MEMBER": "Remover usuário membro",
|
"DELETE_MEMBER": "Remover usuário membro",
|
||||||
|
@ -193,7 +193,8 @@
|
|||||||
"LABELS": "标签",
|
"LABELS": "标签",
|
||||||
"PROJECTS": "项目",
|
"PROJECTS": "项目",
|
||||||
"CONFIG": "配置管理",
|
"CONFIG": "配置管理",
|
||||||
"HELMCHART": "Helm Charts"
|
"HELMCHART": "Helm Charts",
|
||||||
|
"ROBOT_ACCOUNTS": "机器人账户"
|
||||||
},
|
},
|
||||||
"PROJECT_CONFIG": {
|
"PROJECT_CONFIG": {
|
||||||
"REGISTRY": "项目仓库",
|
"REGISTRY": "项目仓库",
|
||||||
@ -257,6 +258,31 @@
|
|||||||
"SET_ROLE": "设置角色",
|
"SET_ROLE": "设置角色",
|
||||||
"REMOVE": "移除成员"
|
"REMOVE": "移除成员"
|
||||||
},
|
},
|
||||||
|
"ROBOT_ACCOUNT": {
|
||||||
|
"NAME": "姓名",
|
||||||
|
"TOKEN": "令牌",
|
||||||
|
"NEW_ROBOT_ACCOUNT": "添加机器人账户",
|
||||||
|
"ENABLED_STATE": "启用状态",
|
||||||
|
"DESCRIPTION": "描述",
|
||||||
|
"ACTION": "操作",
|
||||||
|
"EDIT": "编辑",
|
||||||
|
"OF": "共计",
|
||||||
|
"ITEMS": "条记录",
|
||||||
|
"DISABLE_ACCOUNT": "禁用账户",
|
||||||
|
"ENABLE_ACCOUNT": "启用账户",
|
||||||
|
"DELETE": "删除",
|
||||||
|
"CREAT_ROBOT_ACCOUNT": "创建机器人账户",
|
||||||
|
"PULL_PERMISSION": "Pull 权限",
|
||||||
|
"PUSH_PERMISSION": "Push 权限",
|
||||||
|
"FILTER_PLACEHOLDER": "过滤机器人账户",
|
||||||
|
"ROBOT_NAME": "不能包含特殊字符(~#$%)且长度不能超过255.",
|
||||||
|
"ACCOUNT_EXISTING": "机器人账户已经存在.",
|
||||||
|
"ALERT_TEXT": "这是唯一一次复制您的个人访问令牌的机会",
|
||||||
|
"CREATED_SUCCESS": "创建账户 '{{param}}' 成功.",
|
||||||
|
"COPY_SUCCESS": "成功复制 '{{param}}' 的令牌",
|
||||||
|
"DELETION_TITLE": "删除账户确认",
|
||||||
|
"DELETION_SUMMARY": "你确认删除机器人账户 {{param}}?"
|
||||||
|
},
|
||||||
"GROUP": {
|
"GROUP": {
|
||||||
"GROUP": "组",
|
"GROUP": "组",
|
||||||
"GROUPS": "组",
|
"GROUPS": "组",
|
||||||
@ -800,6 +826,7 @@
|
|||||||
"DELETE_REPO": "删除仓库",
|
"DELETE_REPO": "删除仓库",
|
||||||
"DELETE_TAG": "删除镜像标签",
|
"DELETE_TAG": "删除镜像标签",
|
||||||
"DELETE_USER": "删除用户",
|
"DELETE_USER": "删除用户",
|
||||||
|
"DELETE_ROBOT": "删除账户",
|
||||||
"DELETE_REGISTRY": "删除Registry",
|
"DELETE_REGISTRY": "删除Registry",
|
||||||
"DELETE_REPLICATION": "删除复制",
|
"DELETE_REPLICATION": "删除复制",
|
||||||
"DELETE_MEMBER": "删除用户成员",
|
"DELETE_MEMBER": "删除用户成员",
|
||||||
|
@ -79,3 +79,11 @@ body {
|
|||||||
.datagrid-header{
|
.datagrid-header{
|
||||||
z-index: 1 !important;
|
z-index: 1 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.color-green {
|
||||||
|
color: #1D5100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-red {
|
||||||
|
color: red;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user