certd/packages/ui/certd-server/src/plugins/plugin-notification/webhook/index.ts

169 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { BaseNotification, IsNotification, NotificationBody, NotificationInput } from '@certd/pipeline';
import qs from 'qs';
@IsNotification({
name: 'webhook',
title: '自定义webhook',
desc: '根据模版自定义http请求',
})
export class WebhookNotification extends BaseNotification {
@NotificationInput({
title: 'webhook地址',
component: {
placeholder: 'https://xxxxx.com/xxxx',
},
col: {
span: 24,
},
required: true,
})
webhook = '';
@NotificationInput({
title: '请求方式',
value: 'POST',
component: {
name: 'a-select',
placeholder: 'post/put/get',
options: [
{ value: 'POST', label: 'POST' },
{ value: 'PUT', label: 'PUT' },
{ value: 'GET', label: 'GET' },
],
},
required: true,
})
method = '';
@NotificationInput({
title: 'ContentType',
value: 'application/json',
component: {
name: 'a-auto-complete',
options: [
{ value: 'application/json', label: 'application/json' },
{ value: 'application/x-www-form-urlencoded', label: 'application/x-www-form-urlencoded' },
],
},
helper: '也可以自定义填写',
required: true,
})
contentType = '';
@NotificationInput({
title: 'Headers',
component: {
name: 'a-textarea',
vModel: 'value',
rows: 2,
},
col: {
span: 24,
},
helper: '一行一个格式为key=value',
required: false,
})
headers = '';
@NotificationInput({
title: '消息body模版',
value: `{
"title":"{title}",
"content":"{content}\\n[查看详情]({url})"
}`,
component: {
name: 'a-textarea',
rows: 4,
},
col: {
span: 24,
},
helper: `根据实际的webhook接口构建一个json对象作为参数支持变量{title}、{content}、{url},变量用{}包裹,字符串需要双引号\n如果是get方式将作为query参数拼接到url上`,
required: true,
})
template = '';
@NotificationInput({
title: '忽略证书校验',
value: false,
component: {
name: 'a-switch',
vModel: 'checked',
},
required: false,
})
skipSslVerify: boolean;
replaceTemplate(target: string, body: any, urlEncode = false) {
let bodyStr = target;
const keys = Object.keys(body);
for (const key of keys) {
const value = urlEncode ? encodeURIComponent(body[key]) : body[key];
bodyStr = bodyStr.replaceAll(`{${key}}`, value);
}
return bodyStr;
}
async send(body: NotificationBody) {
if (!this.template) {
throw new Error('模版不能为空');
}
if (!this.webhook) {
throw new Error('webhook不能为空');
}
const replaceBody = {
title: body.title,
content: body.content,
url: body.url,
};
const bodyStr = this.replaceTemplate(this.template, replaceBody);
let data = JSON.parse(bodyStr);
let url = this.webhook;
if (this.method.toLowerCase() === 'get') {
const query = qs.stringify(data);
if (url.includes('?')) {
url = `${url}&${query}`;
} else {
url = `${url}?${query}`;
}
data = null;
}
const headers: any = {};
if (this.headers && this.headers.trim()) {
this.headers.split('\n').forEach(item => {
item = item.trim();
if (item) {
const eqIndex = item.indexOf('=');
if (eqIndex <= 0) {
this.logger.warn('header格式错误,请使用=号分割', item);
return;
}
const key = item.substring(0, eqIndex);
headers[key] = item.substring(eqIndex + 1);
}
});
}
try {
await this.http.request({
url: url,
method: this.method,
headers: {
'Content-Type': `${this.contentType}; charset=UTF-8`,
...headers,
},
data: data,
skipSslVerify: this.skipSslVerify,
});
} catch (e) {
if (e.response?.data) {
throw new Error(e.message + ',' + JSON.stringify(e.response.data));
}
throw e;
}
}
}