Browse Source

Image for webhook #705 (#706)

pull/1/head
Evgeny Smyshlyaev 5 years ago committed by rozetko
parent
commit
15e2cbccb6
  1. 1
      docker-compose.yml
  2. 4
      server/src/config.ts
  3. 58
      server/src/services/alert_service.ts
  4. 6
      server/src/services/notification_service.ts

1
docker-compose.yml

@ -12,6 +12,7 @@ services:
HASTIC_WEBHOOK_TYPE: ${HASTIC_WEBHOOK_TYPE} HASTIC_WEBHOOK_TYPE: ${HASTIC_WEBHOOK_TYPE}
GRAFANA_URL: ${GRAFANA_URL} GRAFANA_URL: ${GRAFANA_URL}
HASTIC_INSTANCE_NAME: ${HASTIC_INSTANCE_NAME} HASTIC_INSTANCE_NAME: ${HASTIC_INSTANCE_NAME}
HASTIC_WEBHOOK_IMAGE_ENABLED: ${HASTIC_WEBHOOK_IMAGE_ENABLED}
ports: ports:
- ${HASTIC_PORT:-8000}:8000 - ${HASTIC_PORT:-8000}:8000
volumes: volumes:

4
server/src/config.ts

@ -29,8 +29,10 @@ export const GRAFANA_URL = normalizeUrl(getConfigField('GRAFANA_URL', null));
// TODO: save orgId in analytic_units.db // TODO: save orgId in analytic_units.db
export const ORG_ID = getConfigField('ORG_ID', 1); export const ORG_ID = getConfigField('ORG_ID', 1);
export const HASTIC_WEBHOOK_URL = getConfigField('HASTIC_WEBHOOK_URL', null); export const HASTIC_WEBHOOK_URL = getConfigField('HASTIC_WEBHOOK_URL', null);
export const HASTIC_WEBHOOK_TYPE = getConfigField('HASTIC_WEBHOOK_TYPE', 'application/x-www-form-urlencoded'); export const HASTIC_WEBHOOK_TYPE = getConfigField('HASTIC_WEBHOOK_TYPE', 'application/json');
export const HASTIC_WEBHOOK_SECRET = getConfigField('HASTIC_WEBHOOK_SECRET', null); export const HASTIC_WEBHOOK_SECRET = getConfigField('HASTIC_WEBHOOK_SECRET', null);
export const HASTIC_WEBHOOK_IMAGE_ENABLED = getConfigField('HASTIC_WEBHOOK_IMAGE', false);
export const ANLYTICS_PING_INTERVAL = 500; // ms export const ANLYTICS_PING_INTERVAL = 500; // ms
export const PACKAGE_VERSION = getPackageVersion(); export const PACKAGE_VERSION = getPackageVersion();
export const GIT_INFO = getGitInfo(); export const GIT_INFO = getGitInfo();

58
server/src/services/alert_service.ts

@ -1,10 +1,11 @@
import { sendNotification, InfoMeta, AnalyticMeta, WebhookType, Notification } from './notification_service'; import { sendNotification, InfoMeta, AnalyticMeta, WebhookType, Notification } from './notification_service';
import * as _ from 'lodash';
import * as AnalyticUnit from '../models/analytic_units'; import * as AnalyticUnit from '../models/analytic_units';
import { Segment } from '../models/segment_model'; import { Segment } from '../models/segment_model';
import { availableReporter } from '../utils/reporter'; import { availableReporter } from '../utils/reporter';
import { ORG_ID } from '../config'; import { ORG_ID, HASTIC_API_KEY, HASTIC_WEBHOOK_IMAGE_ENABLED } from '../config';
import axios from 'axios';
import * as _ from 'lodash';
export class Alert { export class Alert {
@ -12,21 +13,52 @@ export class Alert {
constructor(protected analyticUnit: AnalyticUnit.AnalyticUnit) {}; constructor(protected analyticUnit: AnalyticUnit.AnalyticUnit) {};
public receive(segment: Segment) { public receive(segment: Segment) {
if(this.enabled) { if(this.enabled) {
sendNotification(this.makeNotification(segment)); this.send(segment);
} }
}; };
protected makeNotification(segment: Segment): Notification { protected async send(segment) {
const notification = await this.makeNotification(segment);
sendNotification(notification);
}
protected async makeNotification(segment: Segment): Promise<Notification> {
const meta = this.makeMeta(segment); const meta = this.makeMeta(segment);
const message = this.makeMessage(meta); const message = this.makeMessage(meta);
return { meta, message }; let result: Notification = { meta, message };
if(HASTIC_WEBHOOK_IMAGE_ENABLED) {
try {
const image = await this.loadImage();
result.image = image;
} catch(err) {
console.error(`Can't load alert image: ${err}. Check that API key has admin permissions`);
}
}
return result;
}
protected async loadImage() {
const headers = { Authorization: `Bearer ${HASTIC_API_KEY}` };
const dashdoardId = this.analyticUnit.panelId.split('/')[0];
const panelId = this.analyticUnit.panelId.split('/')[1];
const dashboardApiURL = `${this.analyticUnit.grafanaUrl}/api/dashboards/uid/${dashdoardId}`;
const dashboardInfo: any = await axios.get(dashboardApiURL, { headers });
const dashboardName = _.last(dashboardInfo.data.meta.url.split('/'));
const renderUrl = `${this.analyticUnit.grafanaUrl}/render/d-solo/${dashdoardId}/${dashboardName}?panelId=${panelId}&ordId=${ORG_ID}&api-rendering`;
const response = await axios.get(renderUrl, {
headers,
responseType: 'arraybuffer'
});
return new Buffer(response.data, 'binary').toString('base64');
} }
protected makeMeta(segment: Segment): AnalyticMeta { protected makeMeta(segment: Segment): AnalyticMeta {
const datshdoardId = this.analyticUnit.panelId.split('/')[0]; const dashdoardId = this.analyticUnit.panelId.split('/')[0];
const panelId = this.analyticUnit.panelId.split('/')[1]; const panelId = this.analyticUnit.panelId.split('/')[1];
const grafanaUrl = `${this.analyticUnit.grafanaUrl}/d/${datshdoardId}?panelId=${panelId}&edit=true&fullscreen=true?orgId=${ORG_ID}`; const grafanaUrl = `${this.analyticUnit.grafanaUrl}/d/${dashdoardId}?panelId=${panelId}&edit=true&fullscreen=true?orgId=${ORG_ID}`;
const alert: AnalyticMeta = {
let alert: AnalyticMeta = {
type: WebhookType.DETECT, type: WebhookType.DETECT,
analyticUnitType: this.analyticUnit.type, analyticUnitType: this.analyticUnit.type,
analyticUnitName: this.analyticUnit.name, analyticUnitName: this.analyticUnit.name,
@ -61,7 +93,7 @@ class PatternAlert extends Alert {
if(this.lastSentSegment === undefined || !segment.equals(this.lastSentSegment) ) { if(this.lastSentSegment === undefined || !segment.equals(this.lastSentSegment) ) {
this.lastSentSegment = segment; this.lastSentSegment = segment;
if(this.enabled) { if(this.enabled) {
sendNotification(this.makeNotification(segment)); this.send(segment);
} }
} }
} }
@ -89,14 +121,14 @@ class ThresholdAlert extends Alert {
if(this.lastOccurence === 0) { if(this.lastOccurence === 0) {
this.lastOccurence = segment.from; this.lastOccurence = segment.from;
if(this.enabled) { if(this.enabled) {
sendNotification(this.makeNotification(segment)); this.send(segment);
} }
} else { } else {
if(segment.from - this.lastOccurence > this.EXPIRE_PERIOD_MS) { if(segment.from - this.lastOccurence > this.EXPIRE_PERIOD_MS) {
if(this.enabled) { if(this.enabled) {
console.log(`time between threshold occurences ${segment.from - this.lastOccurence}ms, send alert`); console.log(`time between threshold occurences ${segment.from - this.lastOccurence}ms, send alert`);
sendNotification(this.makeNotification(segment)); this.send(segment);
} }
} }
@ -169,7 +201,7 @@ export class AlertService {
public sendGrafanaAvailableWebhook() { public sendGrafanaAvailableWebhook() {
this._grafanaAvailableReporter(true); this._grafanaAvailableReporter(true);
} }
public sendGrafanaUnavailableWebhook() { public sendGrafanaUnavailableWebhook() {
this._grafanaAvailableReporter(false); this._grafanaAvailableReporter(false);
} }

6
server/src/services/notification_service.ts

@ -25,8 +25,7 @@ export declare type AnalyticMeta = {
grafanaUrl: string, grafanaUrl: string,
from: number, from: number,
to: number to: number
message?: any, message?: any
regionImage?: any
} }
export declare type InfoMeta = { export declare type InfoMeta = {
@ -38,7 +37,8 @@ export declare type InfoMeta = {
export declare type Notification = { export declare type Notification = {
message: string, message: string,
meta: InfoMeta | AnalyticMeta meta: InfoMeta | AnalyticMeta,
image?: any
} }
export async function sendNotification(notification: Notification) { export async function sendNotification(notification: Notification) {

Loading…
Cancel
Save