mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-19 15:17:43 +01:00
Add webhook UI
Signed-off-by: 方彬 <fang_bingo@163.com>
This commit is contained in:
parent
9cbcc93e8a
commit
44ca591582
@ -87,6 +87,7 @@ export class Configuration {
|
||||
token_expiration: NumberValueItem;
|
||||
scan_all_policy: ComplexValueItem;
|
||||
read_only: BoolValueItem;
|
||||
notification_enable: BoolValueItem;
|
||||
http_authproxy_endpoint?: StringValueItem;
|
||||
http_authproxy_tokenreview_endpoint?: StringValueItem;
|
||||
http_authproxy_verify_cert?: BoolValueItem;
|
||||
@ -140,6 +141,7 @@ export class Configuration {
|
||||
}
|
||||
}, true);
|
||||
this.read_only = new BoolValueItem(false, true);
|
||||
this.notification_enable = new BoolValueItem(false, true);
|
||||
this.http_authproxy_endpoint = new StringValueItem("", true);
|
||||
this.http_authproxy_tokenreview_endpoint = new StringValueItem("", true);
|
||||
this.http_authproxy_verify_cert = new BoolValueItem(false, true);
|
||||
|
@ -142,9 +142,21 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="webhookNotificationEnabled">{{'CONFIG.WEBHOOK_NOTIFICATION_ENABLED' | translate}}</label>
|
||||
<clr-checkbox-wrapper>
|
||||
<input type="checkbox" clrCheckbox name="webhookNotificationEnabled" id="webhookNotificationEnabled" [ngModel]="systemSettings.notification_enable.value"
|
||||
(ngModelChange)="setWebhookNotificationEnabledValue($event)" [ngModel]="systemSettings.notification_enable.value"/>
|
||||
<label>
|
||||
<a href="javascript:void(0)" role="tooltip" aria-haspopup="true" class="tooltip tooltip-top-right read-tooltip">
|
||||
<clr-icon shape="info-circle" class="info-tips-icon" size="24"></clr-icon>
|
||||
<span class="tooltip-content">{{'CONFIG.TOOLTIP.WEBHOOK_TOOLTIP' | translate}}</span>
|
||||
</a>
|
||||
</label>
|
||||
</clr-checkbox-wrapper>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
<div>
|
||||
|
@ -108,7 +108,7 @@ export class SystemSettingsComponent implements OnChanges, OnInit {
|
||||
let changes = {};
|
||||
for (let prop in allChanges) {
|
||||
if (prop === 'token_expiration' || prop === 'read_only' || prop === 'project_creation_restriction'
|
||||
|| prop === 'robot_token_duration') {
|
||||
|| prop === 'robot_token_duration' || prop === 'notification_enable') {
|
||||
changes[prop] = allChanges[prop];
|
||||
}
|
||||
}
|
||||
@ -119,6 +119,10 @@ export class SystemSettingsComponent implements OnChanges, OnInit {
|
||||
this.systemSettings.read_only.value = $event;
|
||||
}
|
||||
|
||||
setWebhookNotificationEnabledValue($event: any) {
|
||||
this.systemSettings.notification_enable.value = $event;
|
||||
}
|
||||
|
||||
disabled(prop: any): boolean {
|
||||
return !(prop && prop.editable);
|
||||
}
|
||||
|
@ -23,10 +23,18 @@
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="4">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="confirm()">{{'BUTTON.ENABLE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="5">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-danger" (click)="confirm()">{{'BUTTON.DISABLE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="6">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="isDelete">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="confirm()" [hidden]="isDelete">{{'BUTTON.REPLICATE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="5">
|
||||
<ng-template [ngSwitchCase]="7">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="isDelete">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="confirm()" [hidden]="isDelete">{{'BUTTON.STOP' | translate}}</button>
|
||||
</ng-template>
|
||||
|
@ -144,5 +144,12 @@ export const USERSTATICPERMISSION = {
|
||||
"PUSH": "push"
|
||||
}
|
||||
},
|
||||
"WEBHOOK": {
|
||||
"KEY": "notification-policy",
|
||||
"VALUE": {
|
||||
"LIST": "list",
|
||||
"READ": "read",
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -69,7 +69,7 @@ export const FilterType = {
|
||||
};
|
||||
|
||||
export const enum ConfirmationButtons {
|
||||
CONFIRM_CANCEL, YES_NO, DELETE_CANCEL, CLOSE, REPLICATE_CANCEL, STOP_CANCEL
|
||||
CONFIRM_CANCEL, YES_NO, DELETE_CANCEL, CLOSE, ENABLE_CANCEL, DISABLE_CANCEL, REPLICATE_CANCEL, STOP_CANCEL
|
||||
}
|
||||
export const QuotaUnits = [
|
||||
{
|
||||
|
@ -49,6 +49,7 @@ import { ProjectComponent } from './project/project.component';
|
||||
import { ProjectDetailComponent } from './project/project-detail/project-detail.component';
|
||||
import { MemberComponent } from './project/member/member.component';
|
||||
import { RobotAccountComponent } from './project/robot-account/robot-account.component';
|
||||
import { WebhookComponent } from './project/webhook/webhook.component';
|
||||
import { ProjectLabelComponent } from "./project/project-label/project-label.component";
|
||||
import { ProjectConfigComponent } from './project/project-config/project-config.component';
|
||||
import { ProjectRoutingResolver } from './project/project-routing-resolver.service';
|
||||
@ -207,7 +208,11 @@ const harborRoutes: Routes = [
|
||||
{
|
||||
path: 'tag-retention',
|
||||
component: TagRetentionComponent
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'webhook',
|
||||
component: WebhookComponent
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -28,6 +28,9 @@
|
||||
<li class="nav-item" *ngIf="hasTagRetentionPermission">
|
||||
<a class="nav-link" routerLink="tag-retention" routerLinkActive="active">{{'TAG_RETENTION.TAG_RETENTION' | translate}}</a>
|
||||
</li>
|
||||
<li class="nav-item" *ngIf="hasWebhookListPermission">
|
||||
<a class="nav-link" routerLink="webhook" routerLinkActive="active">{{'PROJECT_DETAIL.WEBHOOKS' | translate}}</a>
|
||||
</li>
|
||||
<li class="nav-item" *ngIf="isSessionValid && (hasConfigurationListPermission)">
|
||||
<a class="nav-link" routerLink="configs" routerLinkActive="active">{{'PROJECT_DETAIL.CONFIG' | translate}}</a>
|
||||
</li>
|
||||
|
@ -44,6 +44,7 @@ export class ProjectDetailComponent implements OnInit {
|
||||
hasConfigurationListPermission: boolean;
|
||||
hasRobotListPermission: boolean;
|
||||
hasTagRetentionPermission: boolean;
|
||||
hasWebhookListPermission: boolean;
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
@ -86,11 +87,12 @@ export class ProjectDetailComponent implements OnInit {
|
||||
USERSTATICPERMISSION.LABEL.KEY, USERSTATICPERMISSION.LABEL.VALUE.CREATE));
|
||||
permissionsList.push(this.userPermissionService.getPermission(projectId,
|
||||
USERSTATICPERMISSION.TAG_RETENTION.KEY, USERSTATICPERMISSION.TAG_RETENTION.VALUE.READ));
|
||||
permissionsList.push(this.userPermissionService.getPermission(projectId,
|
||||
USERSTATICPERMISSION.WEBHOOK.KEY, USERSTATICPERMISSION.WEBHOOK.VALUE.LIST));
|
||||
forkJoin(...permissionsList).subscribe(Rules => {
|
||||
[this.hasProjectReadPermission, this.hasLogListPermission, this.hasConfigurationListPermission, this.hasMemberListPermission
|
||||
, this.hasLabelListPermission, this.hasRepositoryListPermission, this.hasHelmChartsListPermission, this.hasRobotListPermission
|
||||
, this.hasLabelCreatePermission, this.hasTagRetentionPermission] = Rules;
|
||||
|
||||
, this.hasLabelCreatePermission, this.hasTagRetentionPermission, this.hasWebhookListPermission] = Rules;
|
||||
}, error => this.errorHandler.error(error));
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,10 @@ import { AddHttpAuthGroupComponent } from './member/add-http-auth-group/add-http
|
||||
import { TagRetentionComponent } from "./tag-retention/tag-retention.component";
|
||||
import { AddRuleComponent } from "./tag-retention/add-rule/add-rule.component";
|
||||
import { TagRetentionService } from "./tag-retention/tag-retention.service";
|
||||
|
||||
import { WebhookService } from './webhook/webhook.service';
|
||||
import { WebhookComponent } from './webhook/webhook.component';
|
||||
import { AddWebhookComponent } from './webhook/add-webhook/add-webhook.component';
|
||||
import { AddWebhookFormComponent } from './webhook/add-webhook-form/add-webhook-form.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -70,9 +73,12 @@ import { TagRetentionService } from "./tag-retention/tag-retention.service";
|
||||
AddHttpAuthGroupComponent,
|
||||
TagRetentionComponent,
|
||||
AddRuleComponent,
|
||||
WebhookComponent,
|
||||
AddWebhookComponent,
|
||||
AddWebhookFormComponent,
|
||||
],
|
||||
exports: [ProjectComponent, ListProjectComponent],
|
||||
providers: [ProjectRoutingResolver, MemberService, RobotService, TagRetentionService]
|
||||
providers: [ProjectRoutingResolver, MemberService, RobotService, TagRetentionService, WebhookService]
|
||||
})
|
||||
export class ProjectModule {
|
||||
|
||||
|
@ -0,0 +1,46 @@
|
||||
<div class="align-center">
|
||||
<form #webhookForm="ngForm">
|
||||
<section class="form-block webhook-section">
|
||||
<!-- endpoint URL -->
|
||||
<div class="form-group">
|
||||
<label for="edit_endpoint_url" class="col-md-3 form-group-label-override required">{{'WEBHOOK.ENDPOINT_URL' | translate}}</label>
|
||||
<label for="edit_endpoint_url" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left"
|
||||
[class.invalid]="enpointURL.errors && (enpointURL.dirty || enpointURL.touched)" [class.valid]="enpointURL.valid">
|
||||
<input type="text" id="edit_endpoint_url" [disabled]="checking" [(ngModel)]="webhookTarget.address"
|
||||
size="25" name="edit_endpoint_url" #enpointURL="ngModel" required placeholder="http(s)://192.168.1.1">
|
||||
<span class="tooltip-content" *ngIf="enpointURL.errors && enpointURL.errors.required && (enpointURL.dirty || enpointURL.touched)">
|
||||
{{ 'WEBHOOK.URL_IS_REQUIRED' | translate }}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
<!-- auth_header -->
|
||||
<div class="form-group">
|
||||
<label for="auth_header" class="col-md-3 form-group-label-override">{{ 'WEBHOOK.AUTH_HEADER' |
|
||||
translate }}</label>
|
||||
<input type="text" id="auth_header" [disabled]="checking"
|
||||
[(ngModel)]="webhookTarget.auth_header" size="28" name="auth_header">
|
||||
</div>
|
||||
<!-- verify remote cert -->
|
||||
<div class="form-group">
|
||||
<label for="verify_remote_cert">{{'WEBHOOK.VERIFY_REMOTE_CERT' | translate}}</label>
|
||||
<input type="checkbox" [disabled]="checking" clrCheckbox name="verify_remote_cert" id="verify_remote_cert"
|
||||
(ngModelChange)="setCertValue($event)" [ngModel]="!webhookTarget.skip_cert_verify" class="clr-checkbox"/>
|
||||
<clr-tooltip class="icon-tooltip">
|
||||
<clr-icon clrTooltipTrigger shape="info-circle" size="24"></clr-icon>
|
||||
<clr-tooltip-content clrPosition="top-right" clrSize="md" *clrIfOpen>
|
||||
{{'CONFIG.TOOLTIP.VERIFY_REMOTE_CERT' | translate}}
|
||||
</clr-tooltip-content>
|
||||
</clr-tooltip>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
<div *ngIf="!isModify">
|
||||
<button type="button" class="btn btn-primary" [disabled]="!isValid" (click)="onSubmit()">{{'BUTTON.CONTINUE' | translate}}</button>
|
||||
<button type="button" [clrLoading]="checkBtnState" class="btn btn-outline" (click)="onTestEndpoint()" [disabled]="checking || enpointURL.errors">{{'WEBHOOK.TEST_ENDPOINT_BUTTON' | translate}}</button>
|
||||
</div>
|
||||
<div *ngIf="isModify">
|
||||
<button type="button" [clrLoading]="checkBtnState" class="btn btn-outline" (click)="onTestEndpoint()" [disabled]="checking || enpointURL.errors">{{'WEBHOOK.TEST_ENDPOINT_BUTTON' | translate}}</button>
|
||||
<button type="button" class="btn btn-outline" (click)="onCancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" [disabled]="!isValid" (click)="onSubmit()">{{'BUTTON.SAVE' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,12 @@
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.webhook-section {
|
||||
margin-left: calc(50% - 10rem);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.icon-tooltip {
|
||||
margin-top: 4px;
|
||||
}
|
@ -0,0 +1,112 @@
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnChanges,
|
||||
Input,
|
||||
ViewChild,
|
||||
Output,
|
||||
EventEmitter,
|
||||
SimpleChanges
|
||||
} from "@angular/core";
|
||||
import { Webhook, Target } from "../webhook";
|
||||
import { NgForm } from "@angular/forms";
|
||||
import {ClrLoadingState} from "@clr/angular";
|
||||
import { finalize } from "rxjs/operators";
|
||||
import { WebhookService } from "../webhook.service";
|
||||
import { WebhookEventTypes } from '../../../shared/shared.const';
|
||||
import { MessageHandlerService } from "../../../shared/message-handler/message-handler.service";
|
||||
|
||||
@Component({
|
||||
selector: 'add-webhook-form',
|
||||
templateUrl: './add-webhook-form.component.html',
|
||||
styleUrls: ['./add-webhook-form.component.scss']
|
||||
})
|
||||
export class AddWebhookFormComponent implements OnInit, OnChanges {
|
||||
closable: boolean = true;
|
||||
staticBackdrop: boolean = true;
|
||||
checking: boolean = false;
|
||||
checkBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
||||
webhookForm: NgForm;
|
||||
submitting: boolean = false;
|
||||
webhookTarget: Target = new Target();
|
||||
|
||||
@Input() projectId: number;
|
||||
@Input() webhook: Webhook;
|
||||
@Input() isModify: boolean;
|
||||
@Input() isOpen: boolean;
|
||||
@Output() edit = new EventEmitter<boolean>();
|
||||
@Output() close = new EventEmitter<boolean>();
|
||||
@ViewChild("webhookForm") currentForm: NgForm;
|
||||
|
||||
|
||||
constructor(
|
||||
private webhookService: WebhookService,
|
||||
private messageHandlerService: MessageHandlerService
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes['isOpen'] && changes['isOpen'].currentValue) {
|
||||
Object.assign(this.webhookTarget, this.webhook.targets[0]);
|
||||
}
|
||||
}
|
||||
|
||||
onTestEndpoint() {
|
||||
this.checkBtnState = ClrLoadingState.LOADING;
|
||||
this.checking = true;
|
||||
|
||||
this.webhookService
|
||||
.testEndpoint(this.projectId, {
|
||||
targets: [this.webhookTarget]
|
||||
})
|
||||
.pipe(finalize(() => (this.checking = false)))
|
||||
.subscribe(
|
||||
response => {
|
||||
this.checkBtnState = ClrLoadingState.SUCCESS;
|
||||
},
|
||||
error => {
|
||||
this.checkBtnState = ClrLoadingState.DEFAULT;
|
||||
this.messageHandlerService.handleError(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
this.close.emit(false);
|
||||
this.currentForm.reset();
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
const rx = this.isModify
|
||||
? this.webhookService.editWebhook(this.projectId, this.webhook.id, Object.assign(this.webhook, { targets: [this.webhookTarget] }))
|
||||
: this.webhookService.createWebhook(this.projectId, {
|
||||
targets: [this.webhookTarget],
|
||||
event_types: Object.keys(WebhookEventTypes).map(key => WebhookEventTypes[key]),
|
||||
enabled: true,
|
||||
});
|
||||
rx.pipe(finalize(() => (this.submitting = false)))
|
||||
.subscribe(
|
||||
response => {
|
||||
this.edit.emit(this.isModify);
|
||||
},
|
||||
error => {
|
||||
this.messageHandlerService.handleError(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
setCertValue($event: any): void {
|
||||
this.webhookTarget.skip_cert_verify = !$event;
|
||||
}
|
||||
|
||||
public get isValid(): boolean {
|
||||
return (
|
||||
this.currentForm &&
|
||||
this.currentForm.valid &&
|
||||
!this.submitting &&
|
||||
!this.checking
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
<clr-modal [(clrModalOpen)]="isOpen" [clrModalStaticBackdrop]="staticBackdrop" [clrModalClosable]="closable">
|
||||
<h3 class="modal-title">{{'WEBHOOK.EDIT_WEBHOOK' | translate}}</h3>
|
||||
<div class="modal-body">
|
||||
<div>{{'WEBHOOK.EDIT_WEBHOOK_DESC' | translate}}</div>
|
||||
<add-webhook-form [projectId]="projectId"
|
||||
[isModify]="true"
|
||||
[isOpen]="isOpen"
|
||||
[webhook]="webhook"
|
||||
(edit)="closeModal($event)"
|
||||
(close)="closeModal($event)"
|
||||
></add-webhook-form>
|
||||
</div>
|
||||
</clr-modal>
|
@ -0,0 +1,49 @@
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
Input,
|
||||
ViewChild,
|
||||
Output,
|
||||
EventEmitter,
|
||||
} from "@angular/core";
|
||||
import { Webhook } from "../webhook";
|
||||
import { AddWebhookFormComponent } from "../add-webhook-form/add-webhook-form.component";
|
||||
|
||||
@Component({
|
||||
selector: 'add-webhook',
|
||||
templateUrl: './add-webhook.component.html',
|
||||
styleUrls: ['./add-webhook.component.scss']
|
||||
})
|
||||
export class AddWebhookComponent implements OnInit {
|
||||
isOpen: boolean = false;
|
||||
closable: boolean = true;
|
||||
staticBackdrop: boolean = true;
|
||||
|
||||
@Input() projectId: number;
|
||||
@Input() webhook: Webhook;
|
||||
@Output() modify = new EventEmitter<boolean>();
|
||||
@ViewChild(AddWebhookFormComponent)
|
||||
addWebhookFormComponent: AddWebhookFormComponent;
|
||||
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
openAddWebhookModal() {
|
||||
this.isOpen = true;
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
closeModal(isModified: boolean): void {
|
||||
if (isModified) {
|
||||
this.modify.emit(true);
|
||||
}
|
||||
this.isOpen = false;
|
||||
}
|
||||
|
||||
}
|
51
src/portal/src/app/project/webhook/webhook.component.html
Normal file
51
src/portal/src/app/project/webhook/webhook.component.html
Normal file
@ -0,0 +1,51 @@
|
||||
<div class="row">
|
||||
<div *ngIf="!showCreate">
|
||||
<div>
|
||||
<div class="row flex-items-xs-between rightPos">
|
||||
<div class="flex-xs-middle option-left">
|
||||
<div>
|
||||
<span class="endpoint-label">Webhook endpoint</span>: {{endpoint}}
|
||||
<button class="btn btn-link" (click)="openAddWebhookModal()">{{'WEBHOOK.EDIT_BUTTON' | translate}}</button>
|
||||
</div>
|
||||
<div [ngSwitch]="isEnabled">
|
||||
<button *ngSwitchCase="false" class="btn btn-link" (click)="switchWebhookStatus(true)">{{'WEBHOOK.ENABLED_BUTTON' | translate}}</button>
|
||||
<button *ngSwitchCase="true" class="btn btn-link disabled-btn" (click)="switchWebhookStatus(false)">{{'WEBHOOK.DISABLED_BUTTON' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12 datagrid-margin-top">
|
||||
<clr-datagrid [clrDgLoading]="loading">
|
||||
<clr-dg-column>{{'WEBHOOK.TYPE' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'WEBHOOK.STATUS' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'WEBHOOK.CREATED' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'WEBHOOK.LAST_TRIGGERED' | translate}}</clr-dg-column>
|
||||
<clr-dg-row *ngFor="let item of lastTriggers">
|
||||
<clr-dg-cell>{{item.event_type}}</clr-dg-cell>
|
||||
<clr-dg-cell [ngSwitch]="item.enabled">
|
||||
<div *ngSwitchCase="true" class="icon-wrap">
|
||||
<clr-icon shape="check-circle" size="20" class="is-success enabled-icon"></clr-icon>
|
||||
<span>{{'WEBHOOK.ENABLED' | translate}}</span>
|
||||
</div>
|
||||
<div *ngSwitchCase="false" class="icon-wrap">
|
||||
<clr-icon shape="exclamation-triangle" size="20" class="is-warning"></clr-icon>
|
||||
<span>{{'WEBHOOK.DISABLED' | translate}}</span>
|
||||
</div>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>{{item.creation_time | date: 'short'}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{item.last_trigger_time | date: 'short'}}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<span *ngIf="lastTriggerCount">1 - {{lastTriggerCount}} {{'WEBHOOK.OF' | translate}} </span> {{lastTriggerCount}} {{'WEBHOOK.ITEMS' | translate}}
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="showCreate">
|
||||
<h2 class="create-text create-text-title">{{'WEBHOOK.CREATE_WEBHOOK' | translate}}</h2>
|
||||
<p class="create-text">{{'WEBHOOK.CREATE_WEBHOOK_DESC' | translate}}</p>
|
||||
<add-webhook-form class="webhook-form-wrap" [projectId]="projectId" [webhook]="webhook" [isModify]="false" (edit)="editWebhook($event)"></add-webhook-form>
|
||||
</div>
|
||||
<add-webhook [projectId]="projectId" [webhook]="webhook" (modify)="editWebhook($event)"></add-webhook>
|
||||
<confirmation-dialog #confirmationDialogComponent (confirmAction)="confirmSwitch($event)"></confirmation-dialog>
|
||||
</div>
|
37
src/portal/src/app/project/webhook/webhook.component.scss
Normal file
37
src/portal/src/app/project/webhook/webhook.component.scss
Normal file
@ -0,0 +1,37 @@
|
||||
.label-top {
|
||||
top: 12px;
|
||||
}
|
||||
|
||||
.icon-wrap {
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.webhook-form-wrap {
|
||||
width: 19rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.create-text {
|
||||
margin: 0 auto;
|
||||
width: 19rem;
|
||||
}
|
||||
|
||||
.create-text-title {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.endpoint-label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.disabled-btn {
|
||||
color: #e12200;
|
||||
}
|
||||
|
||||
.disabled-btn:hover {
|
||||
color: #c92100;
|
||||
}
|
||||
|
||||
.enabled-icon {
|
||||
margin: -2px 5px 0 0;
|
||||
}
|
154
src/portal/src/app/project/webhook/webhook.component.ts
Normal file
154
src/portal/src/app/project/webhook/webhook.component.ts
Normal file
@ -0,0 +1,154 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// 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 { finalize } from "rxjs/operators";
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { AddWebhookComponent } from "./add-webhook/add-webhook.component";
|
||||
import { AddWebhookFormComponent } from "./add-webhook-form/add-webhook-form.component";
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Webhook, LastTrigger } from './webhook';
|
||||
import { WebhookService } from './webhook.service';
|
||||
import { MessageHandlerService } from "../../shared/message-handler/message-handler.service";
|
||||
import { Project } from '../project';
|
||||
import {
|
||||
ConfirmationTargets,
|
||||
ConfirmationState,
|
||||
ConfirmationButtons
|
||||
} from "../../shared/shared.const";
|
||||
|
||||
import { ConfirmationMessage } from "../../shared/confirmation-dialog/confirmation-message";
|
||||
import { ConfirmationAcknowledgement } from "../../shared/confirmation-dialog/confirmation-state-message";
|
||||
import { ConfirmationDialogComponent } from "../../shared/confirmation-dialog/confirmation-dialog.component";
|
||||
|
||||
@Component({
|
||||
templateUrl: './webhook.component.html',
|
||||
styleUrls: ['./webhook.component.scss'],
|
||||
// changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class WebhookComponent implements OnInit {
|
||||
@ViewChild(AddWebhookComponent)
|
||||
addWebhookComponent: AddWebhookComponent;
|
||||
@ViewChild(AddWebhookFormComponent)
|
||||
addWebhookFormComponent: AddWebhookFormComponent;
|
||||
@ViewChild("confirmationDialogComponent")
|
||||
confirmationDialogComponent: ConfirmationDialogComponent;
|
||||
webhook: Webhook;
|
||||
endpoint: string = '';
|
||||
lastTriggers: LastTrigger[] = [];
|
||||
lastTriggerCount: number = 0;
|
||||
isEnabled: boolean;
|
||||
loading: boolean = false;
|
||||
showCreate: boolean = false;
|
||||
projectId: number;
|
||||
projectName: string;
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private translate: TranslateService,
|
||||
private webhookService: WebhookService,
|
||||
private messageHandlerService: MessageHandlerService) {}
|
||||
|
||||
ngOnInit() {
|
||||
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.getData(this.projectId);
|
||||
}
|
||||
|
||||
getData(projectId: number) {
|
||||
this.getLastTriggers(projectId);
|
||||
this.getWebhook(projectId);
|
||||
}
|
||||
|
||||
getLastTriggers(projectId: number) {
|
||||
this.loading = true;
|
||||
this.webhookService
|
||||
.listLastTrigger(projectId)
|
||||
.pipe(finalize(() => (this.loading = false)))
|
||||
.subscribe(
|
||||
response => {
|
||||
this.lastTriggers = response;
|
||||
this.lastTriggerCount = response.length;
|
||||
},
|
||||
error => {
|
||||
this.messageHandlerService.handleError(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
getWebhook(projectId: number) {
|
||||
this.webhookService
|
||||
.listWebhook(projectId)
|
||||
.subscribe(
|
||||
response => {
|
||||
if (response.length) {
|
||||
this.webhook = response[0];
|
||||
this.endpoint = this.webhook.targets[0].address;
|
||||
this.isEnabled = this.webhook.enabled;
|
||||
this.showCreate = false;
|
||||
} else {
|
||||
this.showCreate = true;
|
||||
}
|
||||
},
|
||||
error => {
|
||||
this.messageHandlerService.handleError(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
switchWebhookStatus(enabled = false) {
|
||||
let content = '';
|
||||
this.translate.get(
|
||||
enabled
|
||||
? 'WEBHOOK.ENABLED_WEBHOOK_SUMMARY'
|
||||
: 'WEBHOOK.DISABLED_WEBHOOK_SUMMARY'
|
||||
).subscribe((res) => content = res + this.projectName);
|
||||
let message = new ConfirmationMessage(
|
||||
enabled ? 'WEBHOOK.ENABLED_WEBHOOK_TITLE' : 'WEBHOOK.DISABLED_WEBHOOK_TITLE',
|
||||
content,
|
||||
'',
|
||||
{},
|
||||
ConfirmationTargets.WEBHOOK,
|
||||
enabled ? ConfirmationButtons.ENABLE_CANCEL : ConfirmationButtons.DISABLE_CANCEL
|
||||
);
|
||||
this.confirmationDialogComponent.open(message);
|
||||
}
|
||||
|
||||
confirmSwitch(message: ConfirmationAcknowledgement) {
|
||||
if (message &&
|
||||
message.source === ConfirmationTargets.WEBHOOK &&
|
||||
message.state === ConfirmationState.CONFIRMED) {
|
||||
this.webhookService
|
||||
.editWebhook(this.projectId, this.webhook.id, Object.assign({}, this.webhook, { enabled: !this.isEnabled }))
|
||||
.subscribe(
|
||||
response => {
|
||||
this.getData(this.projectId);
|
||||
},
|
||||
error => {
|
||||
this.messageHandlerService.handleError(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
editWebhook(isModify: boolean): void {
|
||||
this.getData(this.projectId);
|
||||
}
|
||||
|
||||
openAddWebhookModal(): void {
|
||||
this.addWebhookComponent.openAddWebhookModal();
|
||||
}
|
||||
}
|
56
src/portal/src/app/project/webhook/webhook.service.ts
Normal file
56
src/portal/src/app/project/webhook/webhook.service.ts
Normal file
@ -0,0 +1,56 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// 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 { throwError as observableThrowError, Observable } from "rxjs";
|
||||
import { map, catchError } from "rxjs/operators";
|
||||
import { Injectable } from "@angular/core";
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { Webhook, LastTrigger } from "./webhook";
|
||||
|
||||
@Injectable()
|
||||
export class WebhookService {
|
||||
constructor(private http: HttpClient) { }
|
||||
|
||||
public listWebhook(projectId: number): Observable<Webhook[]> {
|
||||
return this.http
|
||||
.get(`/api/projects/${projectId}/webhook/policies`)
|
||||
.pipe(map(response => response as Webhook[]))
|
||||
.pipe(catchError(error => observableThrowError(error)));
|
||||
}
|
||||
|
||||
public listLastTrigger(projectId: number): Observable<LastTrigger[]> {
|
||||
return this.http
|
||||
.get(`/api/projects/${projectId}/webhook/lasttrigger`)
|
||||
.pipe(map(response => response as LastTrigger[]))
|
||||
.pipe(catchError(error => observableThrowError(error)));
|
||||
}
|
||||
|
||||
public editWebhook(projectId: number, policyId: number, data: any): Observable<any> {
|
||||
return this.http
|
||||
.put(`/api/projects/${projectId}/webhook/policies/${policyId}`, data)
|
||||
.pipe(catchError(error => observableThrowError(error)));
|
||||
}
|
||||
|
||||
public createWebhook(projectId: number, data: any): Observable<any> {
|
||||
return this.http
|
||||
.post(`/api/projects/${projectId}/webhook/policies`, data)
|
||||
.pipe(catchError(error => observableThrowError(error)));
|
||||
}
|
||||
|
||||
|
||||
public testEndpoint(projectId: number, param): Observable<any> {
|
||||
return this.http
|
||||
.post(`/api/projects/${projectId}/webhook/policies/test`, param)
|
||||
.pipe(catchError(error => observableThrowError(error)));
|
||||
}
|
||||
}
|
35
src/portal/src/app/project/webhook/webhook.ts
Normal file
35
src/portal/src/app/project/webhook/webhook.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { WebhookEventTypes } from '../../shared/shared.const';
|
||||
|
||||
export class Webhook {
|
||||
id: number;
|
||||
name: string;
|
||||
project_id: number;
|
||||
description: string;
|
||||
targets: Target[];
|
||||
event_types: WebhookEventTypes[];
|
||||
creator: string;
|
||||
creation_time: Date;
|
||||
update_time: Date;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export class Target {
|
||||
type: string;
|
||||
address: string;
|
||||
attachment: string;
|
||||
auth_header: string;
|
||||
skip_cert_verify: boolean;
|
||||
|
||||
constructor () {
|
||||
this.type = 'http';
|
||||
this.address = '';
|
||||
this.skip_cert_verify = true;
|
||||
}
|
||||
}
|
||||
|
||||
export class LastTrigger {
|
||||
enabled: boolean;
|
||||
event_type: string;
|
||||
creation_time: Date;
|
||||
last_trigger_time: Date;
|
||||
}
|
@ -22,7 +22,15 @@
|
||||
<ng-template [ngSwitchCase]="3">
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="4">
|
||||
<ng-template [ngSwitchCase]="4">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="confirm()">{{'BUTTON.ENABLE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="5">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-danger" (click)="confirm()">{{'BUTTON.DISABLE' | translate}}</button>
|
||||
</ng-template>
|
||||
<ng-template [ngSwitchCase]="6">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()" [hidden]="isDelete">{{'BUTTON.CANCEL' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" [hidden]="isDelete">{{'BUTTON.SWITCH' | translate}}</button>
|
||||
<button type="button" class="btn btn-primary" (click)="cancel()" [disabled]="'true'" [hidden]="!isDelete">{{'BUTTON.CLOSE' | translate}}</button>
|
||||
|
@ -39,7 +39,8 @@ export const enum ConfirmationTargets {
|
||||
CONFIG_ROUTE,
|
||||
CONFIG_TAB,
|
||||
HELM_CHART,
|
||||
HELM_CHART_VERSION
|
||||
HELM_CHART_VERSION,
|
||||
WEBHOOK
|
||||
}
|
||||
|
||||
export const enum ActionType {
|
||||
@ -53,7 +54,7 @@ export const enum ConfirmationState {
|
||||
NA, CONFIRMED, CANCEL
|
||||
}
|
||||
export const enum ConfirmationButtons {
|
||||
CONFIRM_CANCEL, YES_NO, DELETE_CANCEL, CLOSE, SWITCH_CANCEL
|
||||
CONFIRM_CANCEL, YES_NO, DELETE_CANCEL, CLOSE, ENABLE_CANCEL, DISABLE_CANCEL, SWITCH_CANCEL
|
||||
}
|
||||
|
||||
export const ProjectTypes = { 0: 'PROJECT.ALL_PROJECTS', 1: 'PROJECT.PRIVATE_PROJECTS', 2: 'PROJECT.PUBLIC_PROJECTS' };
|
||||
@ -80,3 +81,14 @@ export enum ResourceType {
|
||||
CHART_VERSION = 2,
|
||||
REPOSITORY_TAG = 3,
|
||||
}
|
||||
|
||||
export enum WebhookEventTypes {
|
||||
DOWNLOAD_CHART = "downloadChart",
|
||||
DELETE_CHART = "deleteChart",
|
||||
UPLOAD_CHART = "uploadChart",
|
||||
DELETE_IMAGE = "deleteImage",
|
||||
PULL_IMAGE = "pullImage",
|
||||
PUSH_IMAGE = "pushImage",
|
||||
SCANNING_FAILED = "scanningFailed",
|
||||
SCANNING_COMPLETED = "scanningCompleted",
|
||||
}
|
||||
|
@ -45,7 +45,10 @@
|
||||
"UPLOAD": "Upload",
|
||||
"NO_FILE": "No file selected",
|
||||
"ADD": "ADD",
|
||||
"RUN": "RUN"
|
||||
"RUN": "RUN",
|
||||
"CONTINUE": "CONTINUE",
|
||||
"ENABLE": "ENABLE",
|
||||
"DISABLE": "DISABLE"
|
||||
},
|
||||
"BATCH": {
|
||||
"DELETED_SUCCESS": "Deleted successfully",
|
||||
@ -237,7 +240,8 @@
|
||||
"PROJECTS": "Projects",
|
||||
"CONFIG": "Configuration",
|
||||
"HELMCHART": "Helm Charts",
|
||||
"ROBOT_ACCOUNTS": "Robot Accounts"
|
||||
"ROBOT_ACCOUNTS": "Robot Accounts",
|
||||
"WEBHOOKS": "Webhooks"
|
||||
},
|
||||
"PROJECT_CONFIG": {
|
||||
"REGISTRY": "Project registry",
|
||||
@ -335,6 +339,34 @@
|
||||
"PULL_IS_MUST" : "Pull permission is checked by default and can not be modified.",
|
||||
"EXPORT_TO_FILE" : "export to file"
|
||||
},
|
||||
"WEBHOOK": {
|
||||
"EDIT_BUTTON": "EDIT",
|
||||
"ENABLED_BUTTON": "ENABLED",
|
||||
"DISABLED_BUTTON": "DISABLED",
|
||||
"TYPE": "Webhook",
|
||||
"STATUS": "Status",
|
||||
"CREATED": "Created",
|
||||
"ENABLED": "Enabled",
|
||||
"DISABLED": "Disabled",
|
||||
"OF": "of",
|
||||
"ITEMS": "items",
|
||||
"LAST_TRIGGERED": "Last Triggered",
|
||||
"EDIT_WEBHOOK": "Webhook Endpoint",
|
||||
"CREATE_WEBHOOK": "Getting started with webhooks",
|
||||
"EDIT_WEBHOOK_DESC": "Specify the endpoint for receiving webhook notifications",
|
||||
"CREATE_WEBHOOK_DESC": "To get started with webhooks, provide an endpoint and credentials to access the webhook server.",
|
||||
"ENDPOINT_URL": "Endpoint URL",
|
||||
"URL_IS_REQUIRED": "Endpoint URL is required.",
|
||||
"AUTH_HEADER": "Auth Header",
|
||||
"VERIFY_REMOTE_CERT": "Verify Remote Certificate",
|
||||
"TEST_ENDPOINT_BUTTON": "TEST ENDPOINT",
|
||||
"CANCEL_BUTTON": "CANCEL",
|
||||
"SAVE_BUTTON": "SAVE",
|
||||
"ENABLED_WEBHOOK_TITLE": "Enable Project Webhooks",
|
||||
"ENABLED_WEBHOOK_SUMMARY": "Do you want to enable webhooks for project ",
|
||||
"DISABLED_WEBHOOK_TITLE": "Disable Project Webhooks",
|
||||
"DISABLED_WEBHOOK_SUMMARY": "Do you want to disable webhooks for project "
|
||||
},
|
||||
"GROUP": {
|
||||
"GROUP": "Group",
|
||||
"GROUPS": "Groups",
|
||||
@ -717,6 +749,7 @@
|
||||
"LABEL": "Labels",
|
||||
"REPOSITORY": "Repository",
|
||||
"REPO_READ_ONLY": "Repository Read Only",
|
||||
"WEBHOOK_NOTIFICATION_ENABLED": "Webhooks enabled",
|
||||
"SYSTEM": "System Settings",
|
||||
"PROJECT_QUOTAS": "Project Quotas",
|
||||
"VULNERABILITY": "Vulnerability",
|
||||
@ -769,6 +802,7 @@
|
||||
"VERIFY_CERT": "Verify Cert from LDAP Server",
|
||||
"READONLY_TOOLTIP": "In read-only mode, you can not delete repositories or tags or push images. ",
|
||||
"REPO_TOOLTIP": "Users can not do any operations to the images in this mode.",
|
||||
"WEBHOOK_TOOLTIP": "Enable webhooks to receive callbacks at your designated endpoints when certain actions such as image or chart being pushed, pulled, deleted, scanned are performed",
|
||||
"HOURLY_CRON":"Run once an hour, beginning of hour. Equivalent to 0 0 * * * *.",
|
||||
"WEEKLY_CRON":"Run once a week, midnight between Sat/Sun. Equivalent to 0 0 0 * * 0.",
|
||||
"DAILY_CRON":"Run once a day, midnight. Equivalent to 0 0 0 * * *."
|
||||
|
@ -45,7 +45,10 @@
|
||||
"UPLOAD": "Upload",
|
||||
"NO_FILE": "No file selected",
|
||||
"ADD": "ADD",
|
||||
"RUN": "RUN"
|
||||
"RUN": "RUN",
|
||||
"CONTINUE": "CONTINUE",
|
||||
"ENABLE": "ENABLE",
|
||||
"DISABLE": "DISABLE"
|
||||
},
|
||||
"BATCH": {
|
||||
"DELETED_SUCCESS": "Deleted successfully",
|
||||
@ -238,7 +241,8 @@
|
||||
"PROJECTS": "Proyectos",
|
||||
"CONFIG": "Configuración",
|
||||
"HELMCHART": "Helm Charts",
|
||||
"ROBOT_ACCOUNTS": "Robot Accounts"
|
||||
"ROBOT_ACCOUNTS": "Robot Accounts",
|
||||
"WEBHOOKS": "Webhooks"
|
||||
},
|
||||
"PROJECT_CONFIG": {
|
||||
"REGISTRY": "Registro de proyectos",
|
||||
@ -336,6 +340,34 @@
|
||||
"PULL_IS_MUST" : "Pull permission is checked by default and can not be modified.",
|
||||
"EXPORT_TO_FILE" : "export to file"
|
||||
},
|
||||
"WEBHOOK": {
|
||||
"EDIT_BUTTON": "EDIT",
|
||||
"ENABLED_BUTTON": "ENABLED",
|
||||
"DISABLED_BUTTON": "DISABLED",
|
||||
"TYPE": "Webhook",
|
||||
"STATUS": "Status",
|
||||
"CREATED": "Created",
|
||||
"ENABLED": "Enabled",
|
||||
"DISABLED": "Disabled",
|
||||
"OF": "of",
|
||||
"ITEMS": "items",
|
||||
"LAST_TRIGGERED": "Last Triggered",
|
||||
"EDIT_WEBHOOK": "Webhook Endpoint",
|
||||
"CREATE_WEBHOOK": "Getting started with webhooks",
|
||||
"EDIT_WEBHOOK_DESC": "Specify the endpoint for receiving webhook notifications",
|
||||
"CREATE_WEBHOOK_DESC": "To get started with webhooks, provide an endpoint and credentials to access the webhook server.",
|
||||
"ENDPOINT_URL": "Endpoint URL",
|
||||
"URL_IS_REQUIRED": "Endpoint URL is required.",
|
||||
"AUTH_HEADER": "Auth Header",
|
||||
"VERIFY_REMOTE_CERT": "Verify Remote Certificate",
|
||||
"TEST_ENDPOINT_BUTTON": "TEST ENDPOINT",
|
||||
"CANCEL_BUTTON": "CANCEL",
|
||||
"SAVE_BUTTON": "SAVE",
|
||||
"ENABLED_WEBHOOK_TITLE": "Enable Project Webhooks",
|
||||
"ENABLED_WEBHOOK_SUMMARY": "Do you want to enable webhooks for project ",
|
||||
"DISABLED_WEBHOOK_TITLE": "Disable Project Webhooks",
|
||||
"DISABLED_WEBHOOK_SUMMARY": "Do you want to disable webhooks for project "
|
||||
},
|
||||
"GROUP": {
|
||||
"GROUP": "Group",
|
||||
"GROUPS": "Groups",
|
||||
@ -769,6 +801,7 @@
|
||||
"VERIFY_CERT": "Verify Cert from LDAP Server",
|
||||
"READONLY_TOOLTIP": "In read-only mode, you can not delete repositories or tags or push images. ",
|
||||
"GC_POLICY": "",
|
||||
"WEBHOOK_TOOLTIP": "Enable webhooks to receive callbacks at your designated endpoints when certain actions such as image or chart being pushed, pulled, deleted, scanned are performed",
|
||||
"HOURLY_CRON":"Run once an hour, beginning of hour. Equivalente a 0 0 * * * *.",
|
||||
"WEEKLY_CRON":"Run once a week, midnight between Sat/Sun. Equivalente a 0 0 0 * * 0.",
|
||||
"DAILY_CRON":"Run once a day, midnight. Equivalente a 0 0 0 * * *."
|
||||
|
@ -42,7 +42,10 @@
|
||||
"UPLOAD": "Upload",
|
||||
"NO_FILE": "No file selected",
|
||||
"ADD": "ADD",
|
||||
"RUN": "RUN"
|
||||
"RUN": "RUN",
|
||||
"CONTINUE": "CONTINUE",
|
||||
"ENABLE": "ENABLE",
|
||||
"DISABLE": "DISABLE"
|
||||
},
|
||||
"BATCH": {
|
||||
"DELETED_SUCCESS": "Deleted successfully",
|
||||
@ -231,7 +234,8 @@
|
||||
"PROJECTS": "Projets",
|
||||
"CONFIG": "Configuration",
|
||||
"HELMCHART": "Helm Charts",
|
||||
"ROBOT_ACCOUNTS": "Robot Accounts"
|
||||
"ROBOT_ACCOUNTS": "Robot Accounts",
|
||||
"WEBHOOKS": "Webhooks"
|
||||
},
|
||||
"PROJECT_CONFIG": {
|
||||
"REGISTRY": "Dépôt du Projet",
|
||||
@ -328,6 +332,34 @@
|
||||
"PULL_IS_MUST" : "Pull permission is checked by default and can not be modified.",
|
||||
"EXPORT_TO_FILE" : "export to file"
|
||||
},
|
||||
"WEBHOOK": {
|
||||
"EDIT_BUTTON": "EDIT",
|
||||
"ENABLED_BUTTON": "ENABLED",
|
||||
"DISABLED_BUTTON": "DISABLED",
|
||||
"TYPE": "Webhook",
|
||||
"STATUS": "Status",
|
||||
"CREATED": "Created",
|
||||
"ENABLED": "Enabled",
|
||||
"DISABLED": "Disabled",
|
||||
"OF": "of",
|
||||
"ITEMS": "items",
|
||||
"LAST_TRIGGERED": "Last Triggered",
|
||||
"EDIT_WEBHOOK": "Webhook Endpoint",
|
||||
"CREATE_WEBHOOK": "Getting started with webhooks",
|
||||
"EDIT_WEBHOOK_DESC": "Specify the endpoint for receiving webhook notifications",
|
||||
"CREATE_WEBHOOK_DESC": "To get started with webhooks, provide an endpoint and credentials to access the webhook server.",
|
||||
"ENDPOINT_URL": "Endpoint URL",
|
||||
"URL_IS_REQUIRED": "Endpoint URL is required.",
|
||||
"AUTH_HEADER": "Auth Header",
|
||||
"VERIFY_REMOTE_CERT": "Verify Remote Certificate",
|
||||
"TEST_ENDPOINT_BUTTON": "TEST ENDPOINT",
|
||||
"CANCEL_BUTTON": "CANCEL",
|
||||
"SAVE_BUTTON": "SAVE",
|
||||
"ENABLED_WEBHOOK_TITLE": "Enable Project Webhooks",
|
||||
"ENABLED_WEBHOOK_SUMMARY": "Do you want to enable webhooks for project ",
|
||||
"DISABLED_WEBHOOK_TITLE": "Disable Project Webhooks",
|
||||
"DISABLED_WEBHOOK_SUMMARY": "Do you want to disable webhooks for project "
|
||||
},
|
||||
"GROUP": {
|
||||
"Group": "Group",
|
||||
"GROUPS": "Groups",
|
||||
@ -750,6 +782,7 @@
|
||||
"SCANNING_POLICY": "Définissez la politique d'analyse des images en fonction des différentes exigences. 'Aucune' : pas de politique active; 'Tousles jours à' : déclenchement du balayage à l'heure spécifiée tous les jours.",
|
||||
"READONLY_TOOLTIP": "In read-only mode, you can not delete repositories or tags or push images. ",
|
||||
"GC_POLICY": "",
|
||||
"WEBHOOK_TOOLTIP": "Enable webhooks to receive callbacks at your designated endpoints when certain actions such as image or chart being pushed, pulled, deleted, scanned are performed",
|
||||
"HOURLY_CRON":"Run once an hour, beginning of hour. Équivalent à 0 0 * * * *.",
|
||||
"WEEKLY_CRON":"Run once a week, midnight between Sat/Sun. Équivalent à 0 0 0 * * 0.",
|
||||
"DAILY_CRON":"Run once a day, midnight. Équivalent à 0 0 0 * * *."
|
||||
|
@ -45,7 +45,10 @@
|
||||
"UPLOAD": "Upload",
|
||||
"NO_FILE": "Nenhum arquivo selecionado",
|
||||
"ADD": "ADD",
|
||||
"RUN": "RUN"
|
||||
"RUN": "RUN",
|
||||
"CONTINUE": "CONTINUE",
|
||||
"ENABLE": "ENABLE",
|
||||
"DISABLE": "DISABLE"
|
||||
},
|
||||
"BATCH": {
|
||||
"DELETED_SUCCESS": "Removido com sucesso",
|
||||
@ -235,7 +238,8 @@
|
||||
"PROJECTS": "Projetos",
|
||||
"CONFIG": "Configuração",
|
||||
"HELMCHART": "Helm Charts",
|
||||
"ROBOT_ACCOUNTS": "Robot Accounts"
|
||||
"ROBOT_ACCOUNTS": "Robot Accounts",
|
||||
"WEBHOOKS": "Webhooks"
|
||||
},
|
||||
"PROJECT_CONFIG": {
|
||||
"REGISTRY": "Registro do Projeto",
|
||||
@ -362,6 +366,34 @@
|
||||
"DEVELOPER": "Developer",
|
||||
"GUEST": "Guest"
|
||||
},
|
||||
"WEBHOOK": {
|
||||
"EDIT_BUTTON": "EDIT",
|
||||
"ENABLED_BUTTON": "ENABLED",
|
||||
"DISABLED_BUTTON": "DISABLED",
|
||||
"TYPE": "Webhook",
|
||||
"STATUS": "Status",
|
||||
"CREATED": "Created",
|
||||
"ENABLED": "Enabled",
|
||||
"DISABLED": "Disabled",
|
||||
"OF": "of",
|
||||
"ITEMS": "items",
|
||||
"LAST_TRIGGERED": "Last Triggered",
|
||||
"EDIT_WEBHOOK": "Webhook Endpoint",
|
||||
"CREATE_WEBHOOK": "Getting started with webhooks",
|
||||
"EDIT_WEBHOOK_DESC": "Specify the endpoint for receiving webhook notifications",
|
||||
"CREATE_WEBHOOK_DESC": "To get started with webhooks, provide an endpoint and credentials to access the webhook server.",
|
||||
"ENDPOINT_URL": "Endpoint URL",
|
||||
"URL_IS_REQUIRED": "Endpoint URL is required.",
|
||||
"AUTH_HEADER": "Auth Header",
|
||||
"VERIFY_REMOTE_CERT": "Verify Remote Certificate",
|
||||
"TEST_ENDPOINT_BUTTON": "TEST ENDPOINT",
|
||||
"CANCEL_BUTTON": "CANCEL",
|
||||
"SAVE_BUTTON": "SAVE",
|
||||
"ENABLED_WEBHOOK_TITLE": "Enable Project Webhooks",
|
||||
"ENABLED_WEBHOOK_SUMMARY": "Do you want to enable webhooks for project ",
|
||||
"DISABLED_WEBHOOK_TITLE": "Disable Project Webhooks",
|
||||
"DISABLED_WEBHOOK_SUMMARY": "Do you want to disable webhooks for project "
|
||||
},
|
||||
"AUDIT_LOG": {
|
||||
"USERNAME": "Nome do usuário",
|
||||
"REPOSITORY_NAME": "Nome do repositório",
|
||||
@ -764,6 +796,7 @@
|
||||
"VERIFY_CERT": "Verificar o Certificado do Servidor LDAP",
|
||||
"READONLY_TOOLTIP": "Em modo somente leitura, você não pode remover repositórios ou tags ou enviar imagens. ",
|
||||
"REPO_TOOLTIP": "Usuários não podem efetuar qualquer operação nas imagens nesse modo.",
|
||||
"WEBHOOK_TOOLTIP": "Enable webhooks to receive callbacks at your designated endpoints when certain actions such as image or chart being pushed, pulled, deleted, scanned are performed",
|
||||
"HOURLY_CRON":"Run once an hour, beginning of hour. Equivalente a 0 0 * * * *.",
|
||||
"WEEKLY_CRON":"Run once a week, midnight between Sat/Sun. Equivalente a 0 0 0 * * 0.",
|
||||
"DAILY_CRON":"Run once a day, midnight. Equivalente a 0 0 0 * * *."
|
||||
|
@ -45,7 +45,10 @@
|
||||
"UPLOAD": "上传",
|
||||
"NO_FILE": "未选择文件",
|
||||
"ADD": "添加",
|
||||
"RUN": "执行"
|
||||
"RUN": "执行",
|
||||
"CONTINUE": "继续",
|
||||
"ENABLE": "启用",
|
||||
"DISABLE": "关闭"
|
||||
},
|
||||
"BATCH": {
|
||||
"DELETED_SUCCESS": "删除成功",
|
||||
@ -236,7 +239,8 @@
|
||||
"PROJECTS": "项目",
|
||||
"CONFIG": "配置管理",
|
||||
"HELMCHART": "Helm Charts",
|
||||
"ROBOT_ACCOUNTS": "机器人账户"
|
||||
"ROBOT_ACCOUNTS": "机器人账户",
|
||||
"WEBHOOKS": "Webhooks"
|
||||
},
|
||||
"PROJECT_CONFIG": {
|
||||
"REGISTRY": "项目仓库",
|
||||
@ -334,6 +338,34 @@
|
||||
"PULL_IS_MUST" : "拉取权限默认选中且不可修改。",
|
||||
"EXPORT_TO_FILE" : "导出到文件中"
|
||||
},
|
||||
"WEBHOOK": {
|
||||
"EDIT_BUTTON": "编辑",
|
||||
"ENABLED_BUTTON": "启用",
|
||||
"DISABLED_BUTTON": "停用",
|
||||
"TYPE": "Webhook",
|
||||
"STATUS": "状态",
|
||||
"CREATED": "创建时间",
|
||||
"ENABLED": "启用",
|
||||
"DISABLED": "停用",
|
||||
"OF": "共计",
|
||||
"ITEMS": "条记录",
|
||||
"LAST_TRIGGERED": "最近触发事件",
|
||||
"EDIT_WEBHOOK": "Webhook 目标",
|
||||
"CREATE_WEBHOOK": "创建 Webhooks",
|
||||
"EDIT_WEBHOOK_DESC": "指定接收 Webhook 通知的目标",
|
||||
"CREATE_WEBHOOK_DESC": "为了启用 webhook, 请提供 Endpoint 和凭据以访问 Webhook 服务器。",
|
||||
"ENDPOINT_URL": "Endpoint 地址",
|
||||
"URL_IS_REQUIRED": "Endpoint 地址必填",
|
||||
"AUTH_HEADER": "Auth Header",
|
||||
"VERIFY_REMOTE_CERT": "验证远程证书",
|
||||
"TEST_ENDPOINT_BUTTON": "测试 ENDPOINT",
|
||||
"CANCEL_BUTTON": "取消",
|
||||
"SAVE_BUTTON": "保存",
|
||||
"ENABLED_WEBHOOK_TITLE": "启用项目的 Webhooks",
|
||||
"ENABLED_WEBHOOK_SUMMARY": "你希望开启项目的 Webhooks 吗?",
|
||||
"DISABLED_WEBHOOK_TITLE": "停用项目的 Webhooks",
|
||||
"DISABLED_WEBHOOK_SUMMARY": "你希望停用项目的 Webhooks 吗?"
|
||||
},
|
||||
"GROUP": {
|
||||
"GROUP": "组",
|
||||
"GROUPS": "组",
|
||||
@ -717,6 +749,7 @@
|
||||
"LABEL": "标签",
|
||||
"REPOSITORY": "仓库",
|
||||
"REPO_READ_ONLY": "仓库只读",
|
||||
"WEBHOOK_NOTIFICATION_ENABLED": "开启 WEBHOOK",
|
||||
"SYSTEM": "系统设置",
|
||||
"PROJECT_QUOTAS": "项目定额",
|
||||
"VULNERABILITY": "漏洞",
|
||||
@ -769,6 +802,7 @@
|
||||
"VERIFY_CERT": "检查来自LDAP服务端的证书",
|
||||
"READONLY_TOOLTIP": "选中,表示正在维护状态,不可删除仓库及标签,也不可以推送镜像。",
|
||||
"REPO_TOOLTIP": "用户在此模式下无法对图像执行任何操作。",
|
||||
"WEBHOOK_TOOLTIP": "当执行推送,拉动,删除,扫描图像或图表等特定操作时,启用 webhooks 以在指定端点接收回调",
|
||||
"HOURLY_CRON":"每小时运行一次。相当于 0 0 * * * *",
|
||||
"WEEKLY_CRON":"每周一次,周六/周日午夜之间开始。相当于 0 0 * * * *",
|
||||
"DAILY_CRON":"每天午夜运行一次。相当于 0 0 * * * *"
|
||||
|
Loading…
Reference in New Issue
Block a user