mirror of
https://github.com/goharbor/harbor
synced 2025-05-05 21:59:19 +00:00
Merge pull request #4134 from pengpengshui/batchDelection
Add username and pwd for target and unselected project name and targe…
This commit is contained in:
commit
72cc56100f
src/ui_ng
lib/src
create-edit-endpoint
list-replication-rule
src
app
project/create-project
replication/replication-rule
list-project-model
replication-rule.component.tsreplication-rule.cssreplication-rule.htmlreplication-rule.service.tsreplication-rule.tsuser
i18n/lang
@ -241,15 +241,16 @@ export class CreateEditEndpointComponent implements AfterViewChecked, OnDestroy
|
|||||||
this.onGoing = false;
|
this.onGoing = false;
|
||||||
this.close();
|
this.close();
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
|
this.onGoing = false;
|
||||||
let errorMessageKey = this.handleErrorMessageKey(error.status);
|
let errorMessageKey = this.handleErrorMessageKey(error.status);
|
||||||
this.translateService
|
this.translateService
|
||||||
.get(errorMessageKey)
|
.get(errorMessageKey)
|
||||||
.subscribe(res => {
|
.subscribe(res => {
|
||||||
this.inlineAlert.showInlineError(res);
|
this.inlineAlert.showInlineError(res);
|
||||||
this.onGoing = false;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
this.forceRefreshView(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateEndpoint() {
|
updateEndpoint() {
|
||||||
@ -295,6 +296,7 @@ export class CreateEditEndpointComponent implements AfterViewChecked, OnDestroy
|
|||||||
this.onGoing = false;
|
this.onGoing = false;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
this.forceRefreshView(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleErrorMessageKey(status: number): string {
|
handleErrorMessageKey(status: number): string {
|
||||||
|
@ -13,14 +13,14 @@ export const LIST_REPLICATION_RULE_TEMPLATE: string = `
|
|||||||
<clr-dg-column [clrDgField]="'targets'">{{'REPLICATION.DESTINATION_NAME' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgField]="'targets'">{{'REPLICATION.DESTINATION_NAME' | translate}}</clr-dg-column>
|
||||||
<clr-dg-column [clrDgField]="'trigger'">{{'REPLICATION.TRIGGER_MODE' | translate}}</clr-dg-column>
|
<clr-dg-column [clrDgField]="'trigger'">{{'REPLICATION.TRIGGER_MODE' | translate}}</clr-dg-column>
|
||||||
<clr-dg-placeholder>{{'REPLICATION.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
<clr-dg-placeholder>{{'REPLICATION.PLACEHOLDER' | translate }}</clr-dg-placeholder>
|
||||||
<clr-dg-row *clrDgItems="let p of changedRules" [clrDgItem]="p" [style.backgroundColor]="(projectScope && withReplicationJob && selectedId === p.id) ? '#eee' : ''">
|
<clr-dg-row *clrDgItems="let p of changedRules" [clrDgItem]="p" (click)="selectRule(p)" [style.backgroundColor]="(projectScope && withReplicationJob && selectedId === p.id) ? '#eee' : ''">
|
||||||
<clr-dg-cell (click)="selectRule(p)">{{p.name}}</clr-dg-cell>
|
<clr-dg-cell>{{p.name}}</clr-dg-cell>
|
||||||
<clr-dg-cell *ngIf="!projectScope" (click)="selectRule(p)">
|
<clr-dg-cell *ngIf="!projectScope">
|
||||||
<a href="javascript:void(0)" (click)="redirectTo(p)">{{p.projects?.length>0 ? p.projects[0].name : ''}}</a>
|
<a href="javascript:void(0)">{{p.projects?.length>0 ? p.projects[0].name : ''}}</a>
|
||||||
</clr-dg-cell>
|
</clr-dg-cell>
|
||||||
<clr-dg-cell (click)="selectRule(p)">{{p.description ? p.description : '-'}}</clr-dg-cell>
|
<clr-dg-cell>{{p.description ? p.description : '-'}}</clr-dg-cell>
|
||||||
<clr-dg-cell (click)="selectRule(p)">{{p.targets?.length>0 ? p.targets[0].name : ''}}</clr-dg-cell>
|
<clr-dg-cell>{{p.targets?.length>0 ? p.targets[0].name : ''}}</clr-dg-cell>
|
||||||
<clr-dg-cell (click)="selectRule(p)">{{p.trigger ? p.trigger.kind : ''}}</clr-dg-cell>
|
<clr-dg-cell>{{p.trigger ? p.trigger.kind : ''}}</clr-dg-cell>
|
||||||
</clr-dg-row>
|
</clr-dg-row>
|
||||||
<clr-dg-footer>
|
<clr-dg-footer>
|
||||||
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'REPLICATION.OF' | translate}} </span>{{pagination.totalItems }} {{'REPLICATION.ITEMS' | translate}}
|
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'REPLICATION.OF' | translate}} </span>{{pagination.totalItems }} {{'REPLICATION.ITEMS' | translate}}
|
||||||
|
@ -187,7 +187,6 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
|
|||||||
|
|
||||||
selectRule(rule: ReplicationRule): void {
|
selectRule(rule: ReplicationRule): void {
|
||||||
this.selectedId = rule.id || '';
|
this.selectedId = rule.id || '';
|
||||||
this.selectedRow = null;
|
|
||||||
this.selectOne.emit(rule);
|
this.selectOne.emit(rule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
"clarity-icons": "^0.10.17",
|
"clarity-icons": "^0.10.17",
|
||||||
"clarity-ui": "^0.10.17",
|
"clarity-ui": "^0.10.17",
|
||||||
"core-js": "^2.4.1",
|
"core-js": "^2.4.1",
|
||||||
"harbor-ui": "0.6.30",
|
"harbor-ui": "0.6.32",
|
||||||
"intl": "^1.2.5",
|
"intl": "^1.2.5",
|
||||||
"mutationobserver-shim": "^0.3.2",
|
"mutationobserver-shim": "^0.3.2",
|
||||||
"ngx-cookie": "^1.0.0",
|
"ngx-cookie": "^1.0.0",
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<label for="create_project_name" class="col-md-3 form-group-label-override required">{{'PROJECT.NAME' | translate}}</label>
|
<label for="create_project_name" class="col-md-3 form-group-label-override required">{{'PROJECT.NAME' | translate}}</label>
|
||||||
<label for="create_project_name" aria-haspopup="true" role="tooltip" [class.invalid]="!isNameValid" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left">
|
<label for="create_project_name" aria-haspopup="true" role="tooltip" [class.invalid]="!isNameValid" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left">
|
||||||
<input type="text" id="create_project_name" [(ngModel)]="project.name"
|
<input type="text" id="create_project_name" [(ngModel)]="project.name"
|
||||||
name="create_project_name" size="255"
|
name="create_project_name" size="255" style="width: 296px;"
|
||||||
required
|
required
|
||||||
pattern="^[a-z0-9]+(?:[._-][a-z0-9]+)*$"
|
pattern="^[a-z0-9]+(?:[._-][a-z0-9]+)*$"
|
||||||
minlength="2"
|
minlength="2"
|
||||||
|
2
src/ui_ng/src/app/replication/replication-rule/list-project-model/list-project-model.component.html
2
src/ui_ng/src/app/replication/replication-rule/list-project-model/list-project-model.component.html
@ -1,4 +1,4 @@
|
|||||||
<clr-modal [(clrModalOpen)]="ismodelOpen">
|
<clr-modal [(clrModalOpen)]="ismodelOpen" [clrModalClosable]="false">
|
||||||
<h3 class="modal-title">{{'PROJECT.ALL_PROJECTS' | translate}}</h3>
|
<h3 class="modal-title">{{'PROJECT.ALL_PROJECTS' | translate}}</h3>
|
||||||
<inline-alert class="modal-title" ></inline-alert>
|
<inline-alert class="modal-title" ></inline-alert>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
@ -17,6 +17,7 @@ import {CreateEditEndpointComponent} from "harbor-ui/src/create-edit-endpoint/cr
|
|||||||
|
|
||||||
const ONE_HOUR_SECONDS: number = 3600;
|
const ONE_HOUR_SECONDS: number = 3600;
|
||||||
const ONE_DAY_SECONDS: number = 24 * ONE_HOUR_SECONDS;
|
const ONE_DAY_SECONDS: number = 24 * ONE_HOUR_SECONDS;
|
||||||
|
const FAKE_PASSWORD = 'rjGcfuRu';
|
||||||
|
|
||||||
@Component ({
|
@Component ({
|
||||||
selector: 'repliction-rule',
|
selector: 'repliction-rule',
|
||||||
@ -33,11 +34,12 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
|||||||
weeklySchedule: boolean;
|
weeklySchedule: boolean;
|
||||||
isScheduleOpt: boolean;
|
isScheduleOpt: boolean;
|
||||||
isImmediate: boolean = true;
|
isImmediate: boolean = true;
|
||||||
noProjectInfo: string;
|
noProjectInfo: string = "";
|
||||||
noEndpointInfo: boolean;
|
noSelectedProject: boolean = true;
|
||||||
|
noSelectedEndpoint: boolean = true;
|
||||||
filterCount: number = 0;
|
filterCount: number = 0;
|
||||||
selectedprojectList: Project[] = [];
|
selectedprojectList: Project[] = [];
|
||||||
triggerNames: string[] = ['Immediate', 'Scheduled', 'Manual'];
|
triggerNames: string[] = ['Manual', 'Immediate', 'Scheduled'];
|
||||||
scheduleNames: string[] = ['Daily', 'Weekly'];
|
scheduleNames: string[] = ['Daily', 'Weekly'];
|
||||||
weekly: string[] = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
|
weekly: string[] = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
|
||||||
filterSelect: string[] = ['repository', 'tag'];
|
filterSelect: string[] = ['repository', 'tag'];
|
||||||
@ -50,6 +52,8 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
|||||||
isRuleNameExist: boolean = false;
|
isRuleNameExist: boolean = false;
|
||||||
isSubmitOver: boolean = false;
|
isSubmitOver: boolean = false;
|
||||||
nameChecker: Subject<string> = new Subject<string>();
|
nameChecker: Subject<string> = new Subject<string>();
|
||||||
|
firstEndpointData: { [key: string]: string };
|
||||||
|
realEndpointData: { [key: string]: string } = this.initEndpointData();
|
||||||
|
|
||||||
confirmSub: Subscription;
|
confirmSub: Subscription;
|
||||||
ruleForm: FormGroup;
|
ruleForm: FormGroup;
|
||||||
@ -61,6 +65,7 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
|||||||
@ViewChild(CreateEditEndpointComponent)
|
@ViewChild(CreateEditEndpointComponent)
|
||||||
createEditEndpointComponent: CreateEditEndpointComponent;
|
createEditEndpointComponent: CreateEditEndpointComponent;
|
||||||
|
|
||||||
|
|
||||||
baseFilterData(name: string, option: string[], state: boolean) {
|
baseFilterData(name: string, option: string[], state: boolean) {
|
||||||
return {
|
return {
|
||||||
name: name,
|
name: name,
|
||||||
@ -70,6 +75,13 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initEndpointData(): { [key: string]: string } {
|
||||||
|
return{
|
||||||
|
userName: "",
|
||||||
|
password: ""
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
constructor(public projectService: ProjectService,
|
constructor(public projectService: ProjectService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private fb: FormBuilder,
|
private fb: FormBuilder,
|
||||||
@ -83,11 +95,14 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
|||||||
Promise.all([this.repService.getEndpoints(), this.repService.listProjects()])
|
Promise.all([this.repService.getEndpoints(), this.repService.listProjects()])
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (!res[0]) {
|
if (!res[0]) {
|
||||||
this.noEndpointInfo = true;
|
this.noSelectedEndpoint = true;
|
||||||
}else {
|
}else {
|
||||||
this.targetList = res[0];
|
this.targetList = res[0];
|
||||||
if (!this.policyId) {
|
if (!this.policyId) {
|
||||||
this.setTarget([res[0][0]]);
|
this.setTarget([res[0][0]]);
|
||||||
|
this.realEndpointData.userName = res[0][0].username;
|
||||||
|
this.realEndpointData.password = FAKE_PASSWORD;
|
||||||
|
this.firstEndpointData = Object.assign({}, this.realEndpointData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!res[1]) {
|
if (!res[1]) {
|
||||||
@ -98,6 +113,7 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
if (!this.policyId && this.projectId) {
|
if (!this.policyId && this.projectId) {
|
||||||
this.setProject( res[1].filter(rule => rule.project_id === this.projectId));
|
this.setProject( res[1].filter(rule => rule.project_id === this.projectId));
|
||||||
|
this.noSelectedProject = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!this.policyId) {
|
if (!this.policyId) {
|
||||||
@ -148,7 +164,7 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get isVaild() {
|
get isVaild() {
|
||||||
return !(this.isRuleNameExist || this.noProjectInfo || this.noEndpointInfo || this.inProgress || this.isSubmitOver);
|
return !(this.isRuleNameExist || this.noSelectedProject || this.noSelectedEndpoint || this.inProgress || this.isSubmitOver);
|
||||||
}
|
}
|
||||||
|
|
||||||
createForm() {
|
createForm() {
|
||||||
@ -169,7 +185,6 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
|||||||
replicate_existing_image_now: true,
|
replicate_existing_image_now: true,
|
||||||
replicate_deletion: false
|
replicate_deletion: false
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateForm(rule: ReplicationRule): void {
|
updateForm(rule: ReplicationRule): void {
|
||||||
@ -182,7 +197,14 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
|||||||
replicate_deletion: rule.replicate_deletion
|
replicate_deletion: rule.replicate_deletion
|
||||||
});
|
});
|
||||||
this.setProject(rule.projects);
|
this.setProject(rule.projects);
|
||||||
|
this.noSelectedProject = false;
|
||||||
this.setTarget(rule.targets);
|
this.setTarget(rule.targets);
|
||||||
|
this.noSelectedEndpoint = false;
|
||||||
|
|
||||||
|
this.realEndpointData.userName = rule.targets[0].username;
|
||||||
|
this.realEndpointData.password = FAKE_PASSWORD;
|
||||||
|
this.firstEndpointData = Object.assign({}, this.realEndpointData);
|
||||||
|
|
||||||
if (rule.filters) {
|
if (rule.filters) {
|
||||||
this.setFilter(rule.filters);
|
this.setFilter(rule.filters);
|
||||||
this.updateFilter(rule.filters);
|
this.updateFilter(rule.filters);
|
||||||
@ -250,6 +272,9 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
|||||||
if ($event && $event.target && event.target['value']) {
|
if ($event && $event.target && event.target['value']) {
|
||||||
let selecedTarget: Target = this.targetList.find(target => target.id === +$event.target['value']);
|
let selecedTarget: Target = this.targetList.find(target => target.id === +$event.target['value']);
|
||||||
this.setTarget([selecedTarget]);
|
this.setTarget([selecedTarget]);
|
||||||
|
this.noSelectedEndpoint = false;
|
||||||
|
this.realEndpointData.userName = selecedTarget.username;
|
||||||
|
this.firstEndpointData = Object.assign({}, this.realEndpointData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,7 +283,12 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
selectedProject(project: Project): void {
|
selectedProject(project: Project): void {
|
||||||
this.setProject([project]);
|
if (!project) {
|
||||||
|
this.noSelectedProject = true;
|
||||||
|
}else {
|
||||||
|
this.noSelectedProject = false;
|
||||||
|
this.setProject([project]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addNewFilter(): void {
|
addNewFilter(): void {
|
||||||
@ -308,15 +338,15 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
|||||||
selectTrigger($event: any): void {
|
selectTrigger($event: any): void {
|
||||||
if ($event && $event.target && $event.target['value']) {
|
if ($event && $event.target && $event.target['value']) {
|
||||||
let val: string = $event.target['value'];
|
let val: string = $event.target['value'];
|
||||||
if (val === this.triggerNames[1]) {
|
if (val === this.triggerNames[2]) {
|
||||||
this.isScheduleOpt = true;
|
this.isScheduleOpt = true;
|
||||||
this.isImmediate = false;
|
this.isImmediate = false;
|
||||||
}
|
}
|
||||||
if (val === this.triggerNames[0]) {
|
if (val === this.triggerNames[1]) {
|
||||||
this.isScheduleOpt = false;
|
this.isScheduleOpt = false;
|
||||||
this.isImmediate = true;
|
this.isImmediate = true;
|
||||||
}
|
}
|
||||||
if (val === this.triggerNames[2]) {
|
if (val === this.triggerNames[0]) {
|
||||||
this.isScheduleOpt = false;
|
this.isScheduleOpt = false;
|
||||||
this.isImmediate = false;
|
this.isImmediate = false;
|
||||||
}
|
}
|
||||||
@ -381,7 +411,7 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
|||||||
trigger['schedule_param']['weekday'] = 1;
|
trigger['schedule_param']['weekday'] = 1;
|
||||||
}
|
}
|
||||||
}else {
|
}else {
|
||||||
if (trigger['kind'] === this.triggerNames[2]) {
|
if (trigger['kind'] === this.triggerNames[0]) {
|
||||||
this.isImmediate = false;
|
this.isImmediate = false;
|
||||||
}
|
}
|
||||||
trigger['schedule_param'] = { type: this.scheduleNames[0],
|
trigger['schedule_param'] = { type: this.scheduleNames[0],
|
||||||
@ -412,50 +442,86 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
|
|
||||||
onSubmit() {
|
onSubmit() {
|
||||||
// add new Replication rule
|
|
||||||
this.inProgress = true;
|
this.inProgress = true;
|
||||||
|
let endpointId: string | number = this.ruleForm.value.targets[0].id;
|
||||||
|
let pullData: { [key: string]: string | number } = this.initEndpointData();
|
||||||
|
if (compareValue(this.firstEndpointData, this.realEndpointData)) {
|
||||||
|
this.saveRuleOpe();
|
||||||
|
}else {
|
||||||
|
if (this.realEndpointData.userName !== this.firstEndpointData.userName) {
|
||||||
|
pullData.userName = this.realEndpointData.userName;
|
||||||
|
}else {
|
||||||
|
delete pullData.userName;
|
||||||
|
}
|
||||||
|
if (this.realEndpointData.password !== this.firstEndpointData.password) {
|
||||||
|
pullData.password = this.realEndpointData.password;
|
||||||
|
}else {
|
||||||
|
delete pullData.password;
|
||||||
|
}
|
||||||
|
pullData.id = endpointId;
|
||||||
|
this.repService.pingEndpoint(pullData)
|
||||||
|
.then((res: any) => {
|
||||||
|
delete pullData.id;
|
||||||
|
this.repService.updateEndpoint(endpointId, pullData)
|
||||||
|
.then((res: any) => {
|
||||||
|
this.saveRuleOpe();
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
this.inProgress = false;
|
||||||
|
this.msgHandler.handleError(error);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error: any) => {
|
||||||
|
this.inProgress = false;
|
||||||
|
this.msgHandler.handleError('DESTINATION.TEST_CONNECTION_FAILURE');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveRuleOpe(): void {
|
||||||
|
// add new Replication rule
|
||||||
let copyRuleForm: ReplicationRule = this.ruleForm.value;
|
let copyRuleForm: ReplicationRule = this.ruleForm.value;
|
||||||
copyRuleForm.trigger = this.setTriggerVaule(copyRuleForm.trigger);
|
copyRuleForm.trigger = this.setTriggerVaule(copyRuleForm.trigger);
|
||||||
if (!this.policyId) {
|
if (!this.policyId) {
|
||||||
this.repService.createReplicationRule(copyRuleForm)
|
this.repService.createReplicationRule(copyRuleForm)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.msgHandler.showSuccess('REPLICATION.CREATED_SUCCESS');
|
this.msgHandler.showSuccess('REPLICATION.CREATED_SUCCESS');
|
||||||
this.inProgress = false;
|
this.inProgress = false;
|
||||||
this.isSubmitOver = true;
|
this.isSubmitOver = true;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.copyUpdateForm = Object.assign({}, this.ruleForm.value);
|
this.copyUpdateForm = Object.assign({}, this.ruleForm.value);
|
||||||
if (this.projectId) {
|
if (this.projectId) {
|
||||||
this.router.navigate(['harbor/projects', this.projectId, 'replications']);
|
this.router.navigate(['harbor/projects', this.projectId, 'replications']);
|
||||||
}else {
|
}else {
|
||||||
this.router.navigate(['/harbor/replications']);
|
this.router.navigate(['/harbor/replications']);
|
||||||
}
|
}
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
|
||||||
}).catch((error: any) => {
|
}).catch((error: any) => {
|
||||||
this.inProgress = false;
|
this.inProgress = false;
|
||||||
this.msgHandler.handleError(error);
|
this.msgHandler.handleError(error);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.repService.updateReplicationRule(this.policyId, this.ruleForm.value)
|
this.repService.updateReplicationRule(this.policyId, this.ruleForm.value)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.msgHandler.showSuccess('REPLICATION.UPDATED_SUCCESS');
|
this.msgHandler.showSuccess('REPLICATION.UPDATED_SUCCESS');
|
||||||
this.inProgress = false;
|
this.inProgress = false;
|
||||||
this.isSubmitOver = true;
|
this.isSubmitOver = true;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.copyUpdateForm = Object.assign({}, this.ruleForm.value);
|
this.copyUpdateForm = Object.assign({}, this.ruleForm.value);
|
||||||
if (this.projectId) {
|
if (this.projectId) {
|
||||||
this.router.navigate(['harbor/projects', this.projectId, 'replications']);
|
this.router.navigate(['harbor/projects', this.projectId, 'replications']);
|
||||||
}else {
|
}else {
|
||||||
this.router.navigate(['/harbor/replications']);
|
this.router.navigate(['/harbor/replications']);
|
||||||
}
|
}
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
|
||||||
}).catch((error: any) => {
|
}).catch((error: any) => {
|
||||||
this.inProgress = false;
|
this.inProgress = false;
|
||||||
this.msgHandler.handleError(error);
|
this.msgHandler.handleError(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
openModal() {
|
openModal() {
|
||||||
@ -467,8 +533,10 @@ export class ReplicationRuleComponent implements OnInit, OnDestroy {
|
|||||||
Promise.all([this.repService.getEndpoints()]).then(res => {
|
Promise.all([this.repService.getEndpoints()]).then(res => {
|
||||||
this.targetList = res[0];
|
this.targetList = res[0];
|
||||||
this.setTarget([this.targetList[this.targetList.length - 1]]);
|
this.setTarget([this.targetList[this.targetList.length - 1]]);
|
||||||
|
this.noSelectedEndpoint = false;
|
||||||
|
this.realEndpointData.userName = this.targetList[this.targetList.length - 1].username;
|
||||||
|
this.firstEndpointData = Object.assign({}, this.realEndpointData);
|
||||||
});
|
});
|
||||||
this.noEndpointInfo = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,9 +23,11 @@ label:first-child {
|
|||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
left: -10px !important;
|
left: -10px !important;
|
||||||
}
|
}
|
||||||
.endpointSelect{ width: 290px;}
|
.inputWidth{width: 310px;}
|
||||||
.filterSelect{width: 320px;}
|
.endpointSelect{ width: 290px; margin-right: 34px;}
|
||||||
.filterSelect label{width: 160px;}
|
.filterSelect{width: 350px;}
|
||||||
|
.filterSelect clr-icon{margin-left: 10px;}
|
||||||
|
.filterSelect label{width: 175px;}
|
||||||
.filterSelect label input{width: 100%;}
|
.filterSelect label input{width: 100%;}
|
||||||
.cursor{cursor: pointer;}
|
.cursor{cursor: pointer;}
|
||||||
.pull-left{float: left;}
|
.pull-left{float: left;}
|
||||||
@ -34,7 +36,9 @@ label:first-child {
|
|||||||
.form-group{ min-height: 36px;}
|
.form-group{ min-height: 36px;}
|
||||||
|
|
||||||
.projectInput{float: left;}
|
.projectInput{float: left;}
|
||||||
.projectInput input{width: 185px;background-color: white;}
|
.projectInput input{background-color: white;}
|
||||||
.switchIcon{width:20px;height:20px; margin-top: 2px;margin-left: 5px;}
|
.switchIcon{width:20px;height:20px; margin-top: 16px;margin-left: 10px;}
|
||||||
.addEndpoint{ display: inline-block; line-height: 20px; margin-top: 8px;
|
.addEndpoint{ margin-top: .25em !important;}
|
||||||
vertical-align: bottom; padding-left:10px; cursor: pointer;}
|
.shadow{position: absolute;top: 8px;}
|
||||||
|
.shadow1{width:270px; height: 24px;background-color: #fafafa; z-index: 10; top:5px;}
|
||||||
|
.hoverBg:hover{display: none;}
|
@ -2,37 +2,37 @@
|
|||||||
<a class="cursor" *ngIf="!projectId" (click)="backReplication()">< {{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}}</a>
|
<a class="cursor" *ngIf="!projectId" (click)="backReplication()">< {{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}}</a>
|
||||||
<a class="cursor" *ngIf="projectId" (click)="backProjectReplication()"><{{'SIDE_NAV.PROJECTS' | translate}} {{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate | lowercase}}</a>
|
<a class="cursor" *ngIf="projectId" (click)="backProjectReplication()"><{{'SIDE_NAV.PROJECTS' | translate}} {{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate | lowercase}}</a>
|
||||||
<h1 class="sub-header-title">{{headerTitle | translate}}</h1>
|
<h1 class="sub-header-title">{{headerTitle | translate}}</h1>
|
||||||
<form [formGroup]="ruleForm" (ngSubmit)="onSubmit()" novalidate>
|
<form [formGroup]="ruleForm" novalidate>
|
||||||
<section class="form-block">
|
<section class="form-block">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-4 form-group-label-override">{{'REPLICATION.NAME' | translate}}<span class="colorRed">*</span></label>
|
<label class="col-md-4 form-group-label-override">{{'REPLICATION.NAME' | translate}}<span class="colorRed">*</span></label>
|
||||||
<label class="col-md-8" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left"
|
<label class="col-md-8" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left"
|
||||||
[class.invalid]='(ruleForm.controls.name.touched && ruleForm.controls.name.invalid) || isRuleNameExist'>
|
[class.invalid]='(ruleForm.controls.name.touched && ruleForm.controls.name.invalid) || isRuleNameExist'>
|
||||||
<input type="text" id="ruleName" required formControlName="name" #ruleName (keyup)='checkRuleName()' autocomplete="off">
|
<input type="text" id="ruleName" class="inputWidth" required maxlength="255" formControlName="name" #ruleName (keyup)='checkRuleName()' autocomplete="off">
|
||||||
<span class="tooltip-content">{{ruleNameTooltip | translate}}</span>
|
<span class="tooltip-content">{{ruleNameTooltip | translate}}</span>
|
||||||
</label><span class="spinner spinner-inline spinner-pos" [hidden]="!inNameChecking"></span>
|
</label><span class="spinner spinner-inline spinner-pos" [hidden]="!inNameChecking"></span>
|
||||||
</div>
|
</div>
|
||||||
<!--Description-->
|
<!--Description-->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-4 form-group-label-override">{{'REPLICATION.DESCRIPTION' | translate}}</label>
|
<label class="col-md-4 form-group-label-override">{{'REPLICATION.DESCRIPTION' | translate}}</label>
|
||||||
<textarea type="text" id="ruleDescription" style=" width: 355px;" row= 3; formControlName="description"></textarea>
|
<textarea type="text" id="ruleDescription" class="inputWidth" row= 3; formControlName="description"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<!--Projects-->
|
<!--Projects-->
|
||||||
<h4>{{'REPLICATION.SOURCE' | translate}}</h4>
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-4 form-group-label-override">{{'PROJECT.PROJECTS' | translate}}<span class="colorRed">*</span></label>
|
<label class="col-md-4 form-group-label-override">{{'REPLICATION.SOURCE' | translate}} {{'PROJECT.PROJECTS' | translate | lowercase}}<span class="colorRed">*</span></label>
|
||||||
<div formArrayName="projects">
|
<div formArrayName="projects" [style.visibility]="noSelectedProject?'hidden':'visible'">
|
||||||
<div class="projectInput" *ngFor="let project of projects.controls; let i= index" [formGroupName]="i">
|
<div class="projectInput" *ngFor="let project of projects.controls; let i= index" [formGroupName]="i">
|
||||||
<input formControlName="name" class="label" readonly value="name">
|
<input formControlName="name" type="text" class="inputWidth" disabled value="name">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<clr-icon *ngIf="!(noProjectInfo || projectId)" shape="search" class="is-solid switchIcon" (click)="openProjectModel()"></clr-icon>
|
<clr-icon *ngIf="!(noProjectInfo.length !=0 || projectId)" shape="search" class="is-solid switchIcon" (click)="openProjectModel()"></clr-icon>
|
||||||
<label *ngIf="noProjectInfo" class="colorRed">{{noProjectInfo | translate}}</label>
|
<label *ngIf="noProjectInfo.length != 0" class="colorRed">{{noProjectInfo | translate}}</label>
|
||||||
|
<div class="shadow" [hidden]="!noSelectedProject || noProjectInfo.length != 0"><input type="text" class="inputWidth" disabled ></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--images/Filter-->
|
<!--images/Filter-->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-4 form-group-label-override">{{'REPLICATION.FILTER' | translate}}</label>
|
<label class="col-md-4 form-group-label-override">{{'REPLICATION.SOURCE_IMAGES_FILTER' | translate}}</label>
|
||||||
<div formArrayName="filters">
|
<div formArrayName="filters">
|
||||||
<div class="filterSelect" *ngFor="let filter of filters.controls; let i=index" [formGroupName]="i">
|
<div class="filterSelect" *ngFor="let filter of filters.controls; let i=index" [formGroupName]="i">
|
||||||
<div>
|
<div>
|
||||||
@ -53,43 +53,44 @@
|
|||||||
<clr-icon shape="plus-circle" class="is-solid" [hidden]="isFilterHide" (click)="addNewFilter()" style="margin-top: 11px;"></clr-icon>
|
<clr-icon shape="plus-circle" class="is-solid" [hidden]="isFilterHide" (click)="addNewFilter()" style="margin-top: 11px;"></clr-icon>
|
||||||
</div>
|
</div>
|
||||||
<!--Targets-->
|
<!--Targets-->
|
||||||
<h4>{{'REPLICATION.TARGETS' | translate}}</h4>
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-4 form-group-label-override">{{'DESTINATION.ENDPOINT' | translate}} <span class="colorRed">*</span></label>
|
<label class="col-md-4 form-group-label-override">{{'DESTINATION.ENDPOINT' | translate}} <span class="colorRed">*</span></label>
|
||||||
<div formArrayName="targets">
|
<div formArrayName="targets">
|
||||||
<div class="select endpointSelect pull-left" *ngFor="let target of targets.controls; let i= index" [formGroupName]="i">
|
<div class="select endpointSelect pull-left" *ngFor="let target of targets.controls; let i= index" [formGroupName]="i">
|
||||||
<select id="ruleTarget" (change)="targetChange($event)" formControlName="id">
|
<select id="ruleTarget " class="inputWidth" (mouseenter)="noSelectedEndpoint= false" (change)="targetChange($event)" formControlName="id">
|
||||||
<option *ngFor="let target of targetList" value="{{target.id}}">{{target.name}}: {{target.endpoint}}</option>
|
<option *ngFor="let target of targetList" value="{{target.id}}">{{target.name}}: {{target.endpoint}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<a class="addEndpoint" (click)="openModal()"><clr-icon shape="plus" style="vertical-align: sub;"></clr-icon>{{'REPLICATION.NEW' | translate}}</a>
|
<button class="btn btn-info btn-sm addEndpoint" (click)="openModal()"><clr-icon shape="plus"></clr-icon> {{'REPLICATION.NEW' | translate}}</button>
|
||||||
</div>
|
<div [hidden]="noSelectedEndpoint">userName: <input type="text" [(ngModel)]="realEndpointData.userName" [ngModelOptions]="{standalone:true}"></div>
|
||||||
|
<div [hidden]="noSelectedEndpoint">password: <input type="password" [(ngModel)]="realEndpointData.password" [ngModelOptions]="{standalone:true}"></div>
|
||||||
|
</div>
|
||||||
|
<div class="shadow shadow1 hoverBg" #shadowDiv [hidden]="!noSelectedEndpoint || !targetList.length"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!--Trigger-->
|
<!--Trigger-->
|
||||||
<h4>{{'REPLICATION.TRIGGER' | translate}}</h4>
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-4 form-group-label-override">{{'REPLICATION.MODE' | translate}}</label>
|
<label class="col-md-4 form-group-label-override">{{'REPLICATION.TRIGGER_MODE' | translate}}</label>
|
||||||
<div formGroupName="trigger">
|
<div formGroupName="trigger">
|
||||||
<!--on trigger-->
|
<!--on trigger-->
|
||||||
<div class="select floatSet pull-left">
|
<div class="select floatSet">
|
||||||
<select id="ruleTrigger" formControlName="kind" (change)="selectTrigger($event)">
|
<select id="ruleTrigger" formControlName="kind" (change)="selectTrigger($event)">
|
||||||
|
<option value="Manual">{{'REPLICATION.MANUAL' | translate}}</option>
|
||||||
<option value="Immediate">{{'REPLICATION.IMMEDIATE' | translate}}</option>
|
<option value="Immediate">{{'REPLICATION.IMMEDIATE' | translate}}</option>
|
||||||
<option value="Scheduled">{{'REPLICATION.SCHEDULE' | translate}}</option>
|
<option value="Scheduled">{{'REPLICATION.SCHEDULE' | translate}}</option>
|
||||||
<option value="Manual">{{'REPLICATION.MANUAL' | translate}}</option>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<!--on push-->
|
<!--on push-->
|
||||||
<div style="float: left;" formGroupName="schedule_param">
|
<div formGroupName="schedule_param">
|
||||||
<div class="select floatSet pull-left" [hidden]="!isScheduleOpt">
|
<div class="select floatSet" [hidden]="!isScheduleOpt">
|
||||||
<select name="scheduleType" formControlName="type" (change)="selectSchedule($event)">
|
<select name="scheduleType" formControlName="type" (change)="selectSchedule($event)">
|
||||||
<option value="Daily">{{'REPLICATION.DAILY' | translate}}</option>
|
<option value="Daily">{{'REPLICATION.DAILY' | translate}}</option>
|
||||||
<option value="Weekly">{{'REPLICATION.WEEKLY' | translate}}</option>
|
<option value="Weekly">{{'REPLICATION.WEEKLY' | translate}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<!--weekly-->
|
<!--weekly-->
|
||||||
<span style="float: left;" [hidden]="!weeklySchedule || !isScheduleOpt">on </span>
|
<span [hidden]="!weeklySchedule || !isScheduleOpt">on </span>
|
||||||
<div [hidden]="!weeklySchedule || !isScheduleOpt" class="select floatSet pull-left">
|
<div [hidden]="!weeklySchedule || !isScheduleOpt" class="select floatSet">
|
||||||
<select name="scheduleDay" formControlName="weekday">
|
<select name="scheduleDay" formControlName="weekday">
|
||||||
<option value="1">{{'WEEKLY.MONDAY' | translate}}</option>
|
<option value="1">{{'WEEKLY.MONDAY' | translate}}</option>
|
||||||
<option value="2">{{'WEEKLY.TUESDAY' | translate}}</option>
|
<option value="2">{{'WEEKLY.TUESDAY' | translate}}</option>
|
||||||
@ -110,22 +111,18 @@
|
|||||||
{{'REPLICATION.DELETE_REMOTE_IMAGES' | translate}}
|
{{'REPLICATION.DELETE_REMOTE_IMAGES' | translate}}
|
||||||
</clr-checkbox>
|
</clr-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div style="width: 100%;" >
|
||||||
<!--Setting-->
|
<clr-checkbox [clrChecked]="true" id="ruleExit" formControlName="replicate_existing_image_now">
|
||||||
<div class="form-group">
|
{{'REPLICATION.REPLICATE_IMMEDIATE' | translate}}
|
||||||
<label class="col-md-4 form-group-label-override">{{'REPLICATION.SETTING' | translate}}</label>
|
</clr-checkbox>
|
||||||
<div class="col-lg-7 padLeft0">
|
|
||||||
<clr-checkbox [clrChecked]="true" id="ruleExit" formControlName="replicate_existing_image_now">
|
|
||||||
{{'REPLICATION.REPLICATE_IMMEDIATE' | translate}}
|
|
||||||
</clr-checkbox>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="offset-md-4">
|
<div class="offset-md-4">
|
||||||
<span class="spinner spinner-inline" [hidden]="inProgress === false"></span>
|
<span class="spinner spinner-inline" [hidden]="inProgress === false"></span>
|
||||||
<br>
|
<br>
|
||||||
<button type="button" id="ruleBtnCancel" class="btn btn-outline" [disabled]="!hasFormChange()" (click)="onCancel()">{{ 'BUTTON.CANCEL' | translate }}</button>
|
<button type="button" id="ruleBtnCancel" class="btn btn-outline" [disabled]="!hasFormChange() || this.inProgress || this.isSubmitOver" (click)="onCancel()">{{ 'BUTTON.CANCEL' | translate }}</button>
|
||||||
<button type="submit" id="ruleBtnOk" class="btn btn-primary" [disabled]="!ruleForm.valid || !isVaild || !hasFormChange()">{{ 'BUTTON.OK' | translate }}</button>
|
<button type="submit" id="ruleBtnOk" class="btn btn-primary" (click)="onSubmit()" [disabled]="!ruleForm.valid || !isVaild || !hasFormChange()">{{ 'BUTTON.OK' | translate }}</button>
|
||||||
</div><!-- [disabled]="!ruleForm.valid"-->
|
</div><!-- [disabled]="!ruleForm.valid"-->
|
||||||
</section>
|
</section>
|
||||||
</form>
|
</form>
|
||||||
|
@ -72,4 +72,31 @@ export class ReplicationRuleServie {
|
|||||||
.catch(error => Promise.reject(error));
|
.catch(error => Promise.reject(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public updateEndpoint(endpointId: number | string, endpoint: any): Promise<any> | any {
|
||||||
|
if (!endpointId || endpointId <= 0) {
|
||||||
|
return Promise.reject('Bad request argument.');
|
||||||
|
}
|
||||||
|
if (!endpoint) {
|
||||||
|
return Promise.reject('Invalid endpoint.');
|
||||||
|
}
|
||||||
|
let requestUrl: string = `/api/targets/${endpointId}`;
|
||||||
|
return this.http
|
||||||
|
.put(requestUrl, JSON.stringify(endpoint), HTTP_JSON_OPTIONS)
|
||||||
|
.toPromise()
|
||||||
|
.then(response=>response.status)
|
||||||
|
.catch(error=>Promise.reject(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
public pingEndpoint(endpoint: any): Promise<any> | any {
|
||||||
|
if (!endpoint) {
|
||||||
|
return Promise.reject('Invalid endpoint.');
|
||||||
|
}
|
||||||
|
let requestUrl: string = `/api/targets/ping`;
|
||||||
|
return this.http
|
||||||
|
.post(requestUrl, endpoint, HTTP_JSON_OPTIONS)
|
||||||
|
.toPromise()
|
||||||
|
.then(response=>response.status)
|
||||||
|
.catch(error=>Promise.reject(error));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,26 @@ import {Project} from "../../project/project";
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export class Target {
|
export class Target {
|
||||||
id: 0;
|
id: number;
|
||||||
endpoint: 'string';
|
endpoint: string;
|
||||||
name: 'string';
|
name: string;
|
||||||
username: 'string';
|
username: string;
|
||||||
password: 'string';
|
password: string;
|
||||||
type: 0;
|
type: number;
|
||||||
insecure: true;
|
insecure: true;
|
||||||
creation_time: 'string';
|
creation_time: string;
|
||||||
update_time: 'string';
|
update_time: string;
|
||||||
|
constructor() {
|
||||||
|
this.id = -1;
|
||||||
|
this.endpoint = "";
|
||||||
|
this.name = "";
|
||||||
|
this.username = "";
|
||||||
|
this.password = "";
|
||||||
|
this.type = 0;
|
||||||
|
this.insecure = true;
|
||||||
|
this.creation_time = "";
|
||||||
|
this.update_time = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Filter {
|
export class Filter {
|
||||||
|
@ -277,6 +277,7 @@ export class UserComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
//Refresh the user list
|
//Refresh the user list
|
||||||
refreshUser(from: number, to: number): void {
|
refreshUser(from: number, to: number): void {
|
||||||
|
this.selectedRow = [];
|
||||||
//Start to get
|
//Start to get
|
||||||
this.currentTerm = '';
|
this.currentTerm = '';
|
||||||
this.onGoing = true;
|
this.onGoing = true;
|
||||||
|
@ -324,7 +324,7 @@
|
|||||||
"JOB_LOG_VIEWER": "View Replication Job Log",
|
"JOB_LOG_VIEWER": "View Replication Job Log",
|
||||||
"NO_ENDPOINT_INFO": "Please go to registries and add an endpoint first",
|
"NO_ENDPOINT_INFO": "Please go to registries and add an endpoint first",
|
||||||
"NO_PROJECT_INFO": "Please go to projects and add a project name first",
|
"NO_PROJECT_INFO": "Please go to projects and add a project name first",
|
||||||
"FILTER": "Filter",
|
"SOURCE_IMAGES_FILTER": "Source images filter",
|
||||||
"SCHEDULE": "Scheduled",
|
"SCHEDULE": "Scheduled",
|
||||||
"MANUAL": "Manual",
|
"MANUAL": "Manual",
|
||||||
"IMMEDIATE": "Immediate",
|
"IMMEDIATE": "Immediate",
|
||||||
|
@ -324,7 +324,7 @@
|
|||||||
"JOB_LOG_VIEWER": "View Replication Job Log",
|
"JOB_LOG_VIEWER": "View Replication Job Log",
|
||||||
"NO_ENDPOINT_INFO": "Please go to registries and add an endpoint first",
|
"NO_ENDPOINT_INFO": "Please go to registries and add an endpoint first",
|
||||||
"NO_PROJECT_INFO": "Please go to projects and add a project name first",
|
"NO_PROJECT_INFO": "Please go to projects and add a project name first",
|
||||||
"FILTER": "Filter",
|
"SOURCE_IMAGES_FILTER": "Source images filter",
|
||||||
"SCHEDULE": "Scheduled",
|
"SCHEDULE": "Scheduled",
|
||||||
"MANUAL": "Manual",
|
"MANUAL": "Manual",
|
||||||
"IMMEDIATE": "Immediate",
|
"IMMEDIATE": "Immediate",
|
||||||
|
@ -324,7 +324,7 @@
|
|||||||
"JOB_LOG_VIEWER": "查看复制任务日志",
|
"JOB_LOG_VIEWER": "查看复制任务日志",
|
||||||
"NO_ENDPOINT_INFO": "请先添加目标",
|
"NO_ENDPOINT_INFO": "请先添加目标",
|
||||||
"NO_PROJECT_INFO": "请先去项目添加一个新的项目名称",
|
"NO_PROJECT_INFO": "请先去项目添加一个新的项目名称",
|
||||||
"FILTER": "过滤",
|
"SOURCE_IMAGES_FILTER": "源镜像过滤器",
|
||||||
"SCHEDULE": "定时",
|
"SCHEDULE": "定时",
|
||||||
"MANUAL": "手动",
|
"MANUAL": "手动",
|
||||||
"IMMEDIATE": "即刻",
|
"IMMEDIATE": "即刻",
|
||||||
@ -335,7 +335,7 @@
|
|||||||
"TARGETS":"目标",
|
"TARGETS":"目标",
|
||||||
"MODE": "模式",
|
"MODE": "模式",
|
||||||
"TRIGGER_MODE": "触发模式",
|
"TRIGGER_MODE": "触发模式",
|
||||||
"SOURCE": "资源",
|
"SOURCE": "源",
|
||||||
"REPLICATE": "复制",
|
"REPLICATE": "复制",
|
||||||
"DELETE_REMOTE_IMAGES":"删除本地镜像时同时也删除远程的镜像。",
|
"DELETE_REMOTE_IMAGES":"删除本地镜像时同时也删除远程的镜像。",
|
||||||
"REPLICATE_IMMEDIATE":"立即复制现有的镜像。",
|
"REPLICATE_IMMEDIATE":"立即复制现有的镜像。",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user