2024-11-26 11:12:22 +08:00
|
|
|
|
import { BaseNotification, IsNotification, NotificationBody, NotificationInput } from '@certd/pipeline';
|
2024-11-27 09:50:01 +08:00
|
|
|
|
import qs from 'qs';
|
2025-02-26 21:19:58 +08:00
|
|
|
|
|
2024-11-26 11:12:22 +08:00
|
|
|
|
@IsNotification({
|
|
|
|
|
|
name: 'webhook',
|
|
|
|
|
|
title: '自定义webhook',
|
|
|
|
|
|
desc: '根据模版自定义http请求',
|
2025-10-25 00:09:54 +08:00
|
|
|
|
order: -100,
|
2024-11-26 11:12:22 +08:00
|
|
|
|
})
|
|
|
|
|
|
export class WebhookNotification extends BaseNotification {
|
|
|
|
|
|
@NotificationInput({
|
|
|
|
|
|
title: 'webhook地址',
|
|
|
|
|
|
component: {
|
2024-11-27 09:50:01 +08:00
|
|
|
|
placeholder: 'https://xxxxx.com/xxxx',
|
|
|
|
|
|
},
|
|
|
|
|
|
col: {
|
|
|
|
|
|
span: 24,
|
2024-11-26 11:12:22 +08:00
|
|
|
|
},
|
|
|
|
|
|
required: true,
|
|
|
|
|
|
})
|
|
|
|
|
|
webhook = '';
|
|
|
|
|
|
|
|
|
|
|
|
@NotificationInput({
|
|
|
|
|
|
title: '请求方式',
|
2024-11-27 09:50:01 +08:00
|
|
|
|
value: 'POST',
|
2024-11-26 11:12:22 +08:00
|
|
|
|
component: {
|
|
|
|
|
|
name: 'a-select',
|
2024-11-27 09:50:01 +08:00
|
|
|
|
placeholder: 'post/put/get',
|
2024-11-26 11:12:22 +08:00
|
|
|
|
options: [
|
2024-11-27 09:50:01 +08:00
|
|
|
|
{ value: 'POST', label: 'POST' },
|
|
|
|
|
|
{ value: 'PUT', label: 'PUT' },
|
|
|
|
|
|
{ value: 'GET', label: 'GET' },
|
2024-11-26 11:12:22 +08:00
|
|
|
|
],
|
|
|
|
|
|
},
|
|
|
|
|
|
required: true,
|
|
|
|
|
|
})
|
|
|
|
|
|
method = '';
|
|
|
|
|
|
|
|
|
|
|
|
@NotificationInput({
|
|
|
|
|
|
title: 'ContentType',
|
2024-11-27 09:50:01 +08:00
|
|
|
|
value: 'application/json',
|
2024-11-26 11:12:22 +08:00
|
|
|
|
component: {
|
2024-11-27 09:50:01 +08:00
|
|
|
|
name: 'a-auto-complete',
|
2024-11-26 11:12:22 +08:00
|
|
|
|
options: [
|
2024-11-27 09:50:01 +08:00
|
|
|
|
{ value: 'application/json', label: 'application/json' },
|
|
|
|
|
|
{ value: 'application/x-www-form-urlencoded', label: 'application/x-www-form-urlencoded' },
|
2024-11-26 11:12:22 +08:00
|
|
|
|
],
|
|
|
|
|
|
},
|
2024-11-27 09:50:01 +08:00
|
|
|
|
helper: '也可以自定义填写',
|
2024-11-26 11:12:22 +08:00
|
|
|
|
required: true,
|
|
|
|
|
|
})
|
|
|
|
|
|
contentType = '';
|
|
|
|
|
|
|
2024-11-26 15:13:57 +08:00
|
|
|
|
@NotificationInput({
|
|
|
|
|
|
title: 'Headers',
|
|
|
|
|
|
component: {
|
|
|
|
|
|
name: 'a-textarea',
|
2024-11-26 23:11:20 +08:00
|
|
|
|
vModel: 'value',
|
2024-11-27 09:50:01 +08:00
|
|
|
|
rows: 2,
|
|
|
|
|
|
},
|
|
|
|
|
|
col: {
|
|
|
|
|
|
span: 24,
|
2024-11-26 15:13:57 +08:00
|
|
|
|
},
|
2024-11-27 09:50:01 +08:00
|
|
|
|
helper: '一行一个,格式为key=value',
|
2024-11-26 23:11:20 +08:00
|
|
|
|
required: false,
|
2024-11-26 15:13:57 +08:00
|
|
|
|
})
|
|
|
|
|
|
headers = '';
|
|
|
|
|
|
|
2024-11-26 11:12:22 +08:00
|
|
|
|
@NotificationInput({
|
|
|
|
|
|
title: '消息body模版',
|
|
|
|
|
|
value: `{
|
2024-11-30 17:38:41 +08:00
|
|
|
|
"title":"{title}",
|
|
|
|
|
|
"content":"{content}\\n[查看详情]({url})"
|
2024-11-26 11:12:22 +08:00
|
|
|
|
}`,
|
|
|
|
|
|
component: {
|
|
|
|
|
|
name: 'a-textarea',
|
|
|
|
|
|
rows: 4,
|
|
|
|
|
|
},
|
|
|
|
|
|
col: {
|
|
|
|
|
|
span: 24,
|
|
|
|
|
|
},
|
2025-03-17 18:20:15 +08:00
|
|
|
|
helper: `根据对应的webhook接口文档,构建一个json对象作为参数(默认值只是一个示例,一般不是正确的参数)\n支持变量:{title}、{content}、{url},变量用{}包裹\n字符串需要双引号,使用\\n换行\n如果是get方式,将作为query参数拼接到url上`,
|
2024-11-26 11:12:22 +08:00
|
|
|
|
required: true,
|
|
|
|
|
|
})
|
|
|
|
|
|
template = '';
|
|
|
|
|
|
|
2024-11-27 12:36:28 +08:00
|
|
|
|
@NotificationInput({
|
|
|
|
|
|
title: '忽略证书校验',
|
|
|
|
|
|
value: false,
|
|
|
|
|
|
component: {
|
|
|
|
|
|
name: 'a-switch',
|
|
|
|
|
|
vModel: 'checked',
|
|
|
|
|
|
},
|
|
|
|
|
|
required: false,
|
|
|
|
|
|
})
|
|
|
|
|
|
skipSslVerify: boolean;
|
|
|
|
|
|
|
2024-11-27 09:50:01 +08:00
|
|
|
|
replaceTemplate(target: string, body: any, urlEncode = false) {
|
|
|
|
|
|
let bodyStr = target;
|
|
|
|
|
|
const keys = Object.keys(body);
|
|
|
|
|
|
for (const key of keys) {
|
2025-03-17 18:20:15 +08:00
|
|
|
|
let value = urlEncode ? encodeURIComponent(body[key]) : body[key];
|
|
|
|
|
|
value = value.replaceAll(`\n`, "\\n");
|
2024-11-27 09:50:01 +08:00
|
|
|
|
bodyStr = bodyStr.replaceAll(`{${key}}`, value);
|
2025-03-17 18:20:15 +08:00
|
|
|
|
|
2024-11-27 09:50:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
return bodyStr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-26 11:12:22 +08:00
|
|
|
|
async send(body: NotificationBody) {
|
|
|
|
|
|
if (!this.template) {
|
|
|
|
|
|
throw new Error('模版不能为空');
|
|
|
|
|
|
}
|
2024-11-27 09:50:01 +08:00
|
|
|
|
if (!this.webhook) {
|
|
|
|
|
|
throw new Error('webhook不能为空');
|
|
|
|
|
|
}
|
2024-11-26 11:12:22 +08:00
|
|
|
|
|
2024-11-27 09:50:01 +08:00
|
|
|
|
const replaceBody = {
|
|
|
|
|
|
title: body.title,
|
|
|
|
|
|
content: body.content,
|
|
|
|
|
|
url: body.url,
|
|
|
|
|
|
};
|
|
|
|
|
|
const bodyStr = this.replaceTemplate(this.template, replaceBody);
|
|
|
|
|
|
let data = JSON.parse(bodyStr);
|
2024-11-26 11:12:22 +08:00
|
|
|
|
|
2024-11-27 09:50:01 +08:00
|
|
|
|
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;
|
|
|
|
|
|
}
|
2024-11-26 11:12:22 +08:00
|
|
|
|
|
2024-11-26 15:13:57 +08:00
|
|
|
|
const headers: any = {};
|
2024-11-26 23:11:20 +08:00
|
|
|
|
if (this.headers && this.headers.trim()) {
|
|
|
|
|
|
this.headers.split('\n').forEach(item => {
|
|
|
|
|
|
item = item.trim();
|
|
|
|
|
|
if (item) {
|
2025-02-26 21:19:58 +08:00
|
|
|
|
const eqIndex = item.indexOf('=');
|
|
|
|
|
|
if (eqIndex <= 0) {
|
|
|
|
|
|
this.logger.warn('header格式错误,请使用=号分割', item);
|
2024-11-27 09:50:01 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2025-02-26 21:19:58 +08:00
|
|
|
|
const key = item.substring(0, eqIndex);
|
|
|
|
|
|
headers[key] = item.substring(eqIndex + 1);
|
2024-11-26 23:11:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
2025-03-14 13:16:48 +08:00
|
|
|
|
const res = await this.http.request({
|
2024-11-27 09:50:01 +08:00
|
|
|
|
url: url,
|
2024-11-26 23:11:20 +08:00
|
|
|
|
method: this.method,
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
'Content-Type': `${this.contentType}; charset=UTF-8`,
|
|
|
|
|
|
...headers,
|
|
|
|
|
|
},
|
|
|
|
|
|
data: data,
|
2024-11-27 12:36:28 +08:00
|
|
|
|
skipSslVerify: this.skipSslVerify,
|
2024-11-26 23:11:20 +08:00
|
|
|
|
});
|
2025-03-14 13:16:48 +08:00
|
|
|
|
return res
|
2024-11-26 23:11:20 +08:00
|
|
|
|
} catch (e) {
|
|
|
|
|
|
if (e.response?.data) {
|
|
|
|
|
|
throw new Error(e.message + ',' + JSON.stringify(e.response.data));
|
|
|
|
|
|
}
|
|
|
|
|
|
throw e;
|
|
|
|
|
|
}
|
2024-11-26 11:12:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|