+++ /dev/null
-<ng-template #tasksTpl>
- <!-- Executing -->
- <div *ngFor="let executingTask of executingTasks; trackBy:trackByFn">
- <div class="card tc_task border-0">
- <div class="row no-gutters">
- <div class="col-md-2 text-center">
- <span class="text-info">
- <svg [cdsIcon]="icons.circle"
- [size]="icons.size16"
- ></svg>
- </span>
- </div>
- <div class="col-md-9">
- <div class="card-body p-1">
- <h6 class="card-title bold">{{ executingTask.description }}</h6>
- <div class="mb-1">
- <ngb-progressbar type="info"
- [value]="executingTask?.progress"
- [striped]="true"
- [animated]="true"></ngb-progressbar>
- </div>
- <p class="card-text text-muted">
- <small class="date float-start">
- {{ executingTask.begin_time | cdDate }}
- </small>
-
- <span class="float-end">
- {{ executingTask.progress || 0 }} %
- </span>
- </p>
-
- </div>
- </div>
- </div>
- </div>
-
- <hr>
- </div>
-</ng-template>
-
-<ng-template #notificationsTpl>
- <ng-container *ngIf="notifications.length > 0">
- <button type="button"
- class="btn btn-light btn-block"
- (click)="removeAll(); $event.stopPropagation()">
- <svg [cdsIcon]="icons.trash"
- [size]="icons.size16"
- ></svg>
-
- <ng-container i18n>Clear notifications</ng-container>
- </button>
-
- <hr>
-
- <div *ngFor="let notification of notifications; let i = index"
- [ngClass]="notification.borderClass">
- <div class="card tc_notification border-0">
- <div class="row no-gutters">
- <div class="col-md-2 text-center">
- <span [ngClass]="[icons.stack, icons.large, notification.textClass]">
- <i [ngClass]="[icons.circle, icons.stack2x]"></i>
- <i [ngClass]="[icons.stack1x, icons.inverse, notification.iconClass]"></i>
- </span>
- </div>
- <div class="col-md-10">
- <div class="card-body p-1">
- <button class="btn btn-link float-end mt-0 pt-0"
- title="Remove notification"
- i18n-title
- (click)="remove(i); $event.stopPropagation()">
- <svg [cdsIcon]="icons.trash"
- [size]="icons.size16"
- ></svg>
- </button>
- <button *ngIf="notification.application === 'Prometheus' && notification.type !== 2 && !notification.alertSilenced"
- class="btn btn-link float-end text-muted mute m-0 p-0"
- title="Silence Alert"
- i18n-title
- (click)="silence(notification)">
- <svg [cdsIcon]="icons.mute"
- [size]="icons.size16"
- ></svg>
- </button>
- <button *ngIf="notification.application === 'Prometheus' && notification.type !== 2 && notification.alertSilenced"
- class="btn btn-link float-end text-muted mute m-0 p-0"
- title="Expire Silence"
- i18n-title
- (click)="expire(notification)">
- <svg [cdsIcon]="icons.bell"
- [size]="icons.size16"
- ></svg>
- </button>
-
-
- <h6 class="card-title bold">{{ notification.title }}</h6>
- <p class="card-text"
- [innerHtml]="notification.message"></p>
- <p class="card-text text-muted">
- <ng-container *ngIf="notification.duration">
- <small>
- <ng-container i18n>Duration:</ng-container> {{ notification.duration | duration }}
- </small>
- <br>
- </ng-container>
- <small class="date"
- [title]="notification.timestamp | cdDate">{{ notification.timestamp | relativeDate }}</small>
- <i class="float-end custom-icon"
- [ngClass]="[notification.applicationClass]"
- [title]="notification.application"></i>
- </p>
- </div>
- </div>
- </div>
- </div>
-
- <hr>
- </div>
- </ng-container>
-</ng-template>
-
-<ng-template #emptyTpl>
- <div *ngIf="notifications.length === 0 && executingTasks.length === 0">
- <div class="message text-center"
- i18n>There are no notifications.</div>
- </div>
-</ng-template>
-
-<div class="card"
- (clickOutside)="closeSidebar()"
- [clickOutsideEnabled]="isSidebarOpened">
- <div class="card-header">
- <ng-container i18n>Tasks and Notifications</ng-container>
-
- <button class="btn-close float-end"
- tabindex="-1"
- type="button"
- title="close"
- (click)="closeSidebar()">
- </button>
- </div>
-
- <ngx-simplebar [options]="simplebar">
- <div class="card-body">
- <ng-container *ngTemplateOutlet="tasksTpl"></ng-container>
- <ng-container *ngTemplateOutlet="notificationsTpl"></ng-container>
- <ng-container *ngTemplateOutlet="emptyTpl"></ng-container>
- </div>
- </ngx-simplebar>
-</div>
+++ /dev/null
-import {
- ChangeDetectionStrategy,
- ChangeDetectorRef,
- Component,
- HostBinding,
- NgZone,
- OnDestroy,
- OnInit
-} from '@angular/core';
-
-import { Mutex } from 'async-mutex';
-import _ from 'lodash';
-import moment from 'moment';
-import { Subscription } from 'rxjs';
-
-import { PrometheusService } from '~/app/shared/api/prometheus.service';
-import { SucceededActionLabelsI18n } from '~/app/shared/constants/app.constants';
-import { Icons } from '~/app/shared/enum/icons.enum';
-import { NotificationType } from '~/app/shared/enum/notification-type.enum';
-import {
- AlertmanagerSilence,
- AlertmanagerSilenceMatcher
-} from '~/app/shared/models/alertmanager-silence';
-import { CdNotification } from '~/app/shared/models/cd-notification';
-import { ExecutingTask } from '~/app/shared/models/executing-task';
-import { FinishedTask } from '~/app/shared/models/finished-task';
-import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
-import { NotificationService } from '~/app/shared/services/notification.service';
-import { PrometheusAlertService } from '~/app/shared/services/prometheus-alert.service';
-import { PrometheusNotificationService } from '~/app/shared/services/prometheus-notification.service';
-import { SummaryService } from '~/app/shared/services/summary.service';
-import { TaskMessageService } from '~/app/shared/services/task-message.service';
-
-@Component({
- selector: 'cd-notifications-sidebar',
- templateUrl: './notifications-sidebar.component.html',
- styleUrls: ['./notifications-sidebar.component.scss'],
- changeDetection: ChangeDetectionStrategy.OnPush,
- standalone: false
-})
-export class NotificationsSidebarComponent implements OnInit, OnDestroy {
- @HostBinding('class.active') isSidebarOpened = false;
-
- notifications: CdNotification[];
- private interval: number;
- private timeout: number;
-
- executingTasks: ExecutingTask[] = [];
-
- private subs = new Subscription();
-
- icons = Icons;
-
- // Tasks
- last_task = '';
- mutex = new Mutex();
-
- simplebar = {
- autoHide: false
- };
-
- constructor(
- public notificationService: NotificationService,
- private summaryService: SummaryService,
- private taskMessageService: TaskMessageService,
- private prometheusNotificationService: PrometheusNotificationService,
- private succeededLabels: SucceededActionLabelsI18n,
- private authStorageService: AuthStorageService,
- private prometheusAlertService: PrometheusAlertService,
- private prometheusService: PrometheusService,
- private ngZone: NgZone,
- private cdRef: ChangeDetectorRef
- ) {
- this.notifications = [];
- }
-
- ngOnDestroy() {
- window.clearInterval(this.interval);
- window.clearTimeout(this.timeout);
- this.subs.unsubscribe();
- }
-
- ngOnInit() {
- this.last_task = window.localStorage.getItem('last_task');
-
- const permissions = this.authStorageService.getPermissions();
- if (permissions.prometheus.read && permissions.configOpt.read) {
- this.triggerPrometheusAlerts();
- this.ngZone.runOutsideAngular(() => {
- this.interval = window.setInterval(() => {
- this.ngZone.run(() => {
- this.triggerPrometheusAlerts();
- });
- }, 5000);
- });
- }
-
- this.subs.add(
- this.notificationService.data$.subscribe((notifications: CdNotification[]) => {
- this.notifications = _.orderBy(notifications, ['timestamp'], ['desc']);
- this.cdRef.detectChanges();
- })
- );
-
- this.subs.add(
- this.notificationService.panelState$.subscribe((state) => {
- this.isSidebarOpened = state;
- this.cdRef.detectChanges();
- })
- );
-
- this.subs.add(
- this.summaryService.subscribe((summary) => {
- this._handleTasks(summary.executing_tasks);
-
- this.mutex.acquire().then((release) => {
- _.filter(
- summary.finished_tasks,
- (task: FinishedTask) => !this.last_task || moment(task.end_time).isAfter(this.last_task)
- ).forEach((task) => {
- const config = this.notificationService.finishedTaskToNotification(task, task.success);
- const notification = new CdNotification(config);
- notification.timestamp = task.end_time;
- notification.duration = task.duration;
-
- if (!this.last_task || moment(task.end_time).isAfter(this.last_task)) {
- this.last_task = task.end_time;
- window.localStorage.setItem('last_task', this.last_task);
- }
-
- this.notificationService.save(notification);
- });
-
- this.cdRef.detectChanges();
-
- release();
- });
- })
- );
- }
-
- _handleTasks(executingTasks: ExecutingTask[]) {
- for (const executingTask of executingTasks) {
- executingTask.description = this.taskMessageService.getRunningTitle(executingTask);
- }
- this.executingTasks = executingTasks;
- }
-
- private triggerPrometheusAlerts() {
- this.prometheusAlertService.refresh();
- this.prometheusNotificationService.refresh();
- }
-
- removeAll() {
- this.notificationService.removeAll();
- }
-
- remove(index: number) {
- this.notificationService.remove(index);
- }
-
- closeSidebar() {
- this.notificationService.togglePanel(false);
- }
-
- trackByFn(index: number) {
- return index;
- }
-
- silence(data: CdNotification) {
- const datetimeFormat = 'YYYY-MM-DD HH:mm';
- const resource = $localize`silence`;
- const matcher: AlertmanagerSilenceMatcher = {
- name: 'alertname',
- value: data['title'].split(' ')[0],
- isRegex: false
- };
- const silencePayload: AlertmanagerSilence = {
- matchers: [matcher],
- startsAt: moment(moment().format(datetimeFormat)).toISOString(),
- endsAt: moment(moment().add(2, 'hours').format(datetimeFormat)).toISOString(),
- createdBy: this.authStorageService.getUsername(),
- comment: 'Silence created from the alert notification'
- };
- let msg = '';
-
- data.alertSilenced = true;
- msg = msg.concat(` ${matcher.name} - ${matcher.value},`);
- const title = `${this.succeededLabels.CREATED} ${resource} for ${msg.slice(0, -1)}`;
- this.prometheusService.setSilence(silencePayload).subscribe((resp) => {
- if (data) {
- data.silenceId = resp.body['silenceId'];
- }
- this.notificationService.show(
- NotificationType.success,
- title,
- undefined,
- undefined,
- 'Prometheus'
- );
- });
- }
-
- expire(data: CdNotification) {
- data.alertSilenced = false;
- this.prometheusService.expireSilence(data.silenceId).subscribe(
- () => {
- this.notificationService.show(
- NotificationType.success,
- `${this.succeededLabels.EXPIRED} ${data.silenceId}`,
- undefined,
- undefined,
- 'Prometheus'
- );
- },
- (resp) => {
- resp['application'] = 'Prometheus';
- }
- );
- }
-}