修复自定义通知

This commit is contained in:
whyour 2022-09-20 17:34:36 +08:00
parent 5168b044a3
commit 1ebcfd8001
5 changed files with 125 additions and 27 deletions

View File

@ -95,6 +95,7 @@ export class WebhookNotification extends NotificationBaseInfo {
public webhookBody: any = {};
public webhookUrl: string = '';
public webhookMethod: 'GET' | 'POST' | 'PUT' = 'GET';
public webhookContentType: 'application/json' | 'multipart/form-data' | 'application/x-www-form-urlencoded' = 'application/json';
}
export interface NotificationInfo

View File

@ -34,7 +34,7 @@ export default class NotificationService {
private content = '';
private params!: Omit<NotificationInfo, 'type'>;
constructor(@Inject('logger') private logger: winston.Logger) {}
constructor(@Inject('logger') private logger: winston.Logger) { }
public async notify(
title: string,
@ -181,9 +181,8 @@ export default class NotificationService {
telegramBotUserId,
} = this.params;
const authStr = telegramBotProxyAuth ? `${telegramBotProxyAuth}@` : '';
const url = `https://${
telegramBotApiHost ? telegramBotApiHost : 'api.telegram.org'
}/bot${telegramBotToken}/sendMessage`;
const url = `https://${telegramBotApiHost ? telegramBotApiHost : 'api.telegram.org'
}/bot${telegramBotToken}/sendMessage`;
let agent;
if (telegramBotProxyHost && telegramBotProxyPort) {
const options: any = {
@ -386,17 +385,32 @@ export default class NotificationService {
}
private async webhook() {
const { webhookUrl, webhookBody, webhookHeaders, webhookMethod } =
const { webhookUrl, webhookBody, webhookHeaders, webhookMethod, webhookContentType } =
this.params;
const { statusCode } = await got(webhookUrl, {
const bodyParam = this.formatBody(webhookContentType, webhookBody);
const options = {
method: webhookMethod,
headers: webhookHeaders,
body: webhookBody,
timeout: this.timeout,
retry: 0,
});
allowGetBody: true,
...bodyParam
}
const res = await got(webhookUrl, options);
return String(res.statusCode).startsWith('20');
}
return String(statusCode).includes('20');
private formatBody(contentType: string, body: any): object {
if (!body) return {};
switch (contentType) {
case 'application/json':
return { json: body };
case 'multipart/form-data':
return { form: body };
case 'application/x-www-form-urlencoded':
return { body };
}
return {};
}
}

View File

@ -2,6 +2,7 @@ import React, { useEffect, useState } from 'react';
import { Typography, Input, Form, Button, Select, message } from 'antd';
import { request } from '@/utils/http';
import config from '@/utils/config';
import { parseBody, parseHeaders } from '@/utils';
const { Option } = Select;
@ -12,9 +13,15 @@ const NotificationSetting = ({ data }: any) => {
const [form] = Form.useForm();
const handleOk = (values: any) => {
if (values.type == 'closed') {
const { type, webhookBody, webhookContentType } = values;
if (type == 'closed') {
values.type = '';
}
if (type === 'webhook') {
values.webhookHeaders = { ...parseHeaders(values.webhookHeaders) };
values.webhookBody = parseBody(webhookBody, webhookContentType);
}
request
.put(`${config.apiPrefix}user/notification`, {
data: {
@ -73,10 +80,22 @@ const NotificationSetting = ({ data }: any) => {
rules={[{ required: x.required }]}
style={{ maxWidth: 400 }}
>
<Input.TextArea
autoSize={true}
placeholder={x.placeholder || `请输入${x.label}`}
/>
{
x.items ? (
<Select placeholder={x.placeholder || `请选择${x.label}`}>
{x.items.map((y) => (
<Option key={y.value} value={y.value}>
{y.label || y.value}
</Option>
))}
</Select>
) : (
<Input.TextArea
autoSize={true}
placeholder={x.placeholder || `请输入${x.label}`}
/>
)
}
</Form.Item>
))}
<Button type="primary" htmlType="submit">

View File

@ -206,7 +206,7 @@ export default {
pushPlus: [
{
label: 'pushPlusToken',
tip: '微信扫码登录后一对一推送或一对多推送下面的token(您的Token)不提供PUSH_PLUS_USER则默认为一对一推送',
tip: '微信扫码登录后一对一推送或一对多推送下面的token(您的Token)不提供PUSH_PLUS_USER则默认为一对一推送,参考 https://www.pushplus.plus/',
required: true,
},
{
@ -228,23 +228,29 @@ export default {
label: 'webhookMethod',
tip: '请求方法',
required: true,
placeholder: '请输入 GET/POST/PUT',
items: [{ value: 'GET' }, { value: 'POST' }, { value: 'PUT' }],
},
{
label: 'webhookContentType',
tip: '请求头Content-Type',
required: true,
items: [{ value: 'application/json' }, { value: 'multipart/form-data' }, { value: 'application/x-www-form-urlencoded' }],
},
{
label: 'webhookUrl',
tip: '请求链接',
tip: '请求链接以http或者https开头',
required: true,
placeholder: 'https://xxx.cn/api?query=xxx',
placeholder: 'https://xxx.cn/api?query=xxx\n',
},
{
label: 'webhookHeaders',
tip: '请求头',
placeholder: '{"Custom-Header": "$Header"}',
tip: '请求头格式Custom-Header1: Header1多个换行分割',
placeholder: 'Custom-Header1: Header1\nCustom-Header2: Header2',
},
{
label: 'webhookBody',
tip: '请求体',
placeholder: '{"status": "$STATUS"}',
tip: '请求体格式key1: value1多个换行分割',
placeholder: 'key1: value1\nkey2: value2',
},
],
},

View File

@ -150,9 +150,9 @@ export default function browserType() {
shell === 'none'
? {}
: {
shell, // wechat qq uc 360 2345 sougou liebao maxthon
shellVs,
},
shell, // wechat qq uc 360 2345 sougou liebao maxthon
shellVs,
},
);
console.log(
@ -188,8 +188,8 @@ export function getTableScroll({
if (id) {
tHeader = document.getElementById(id)
? document
.getElementById(id)!
.getElementsByClassName('ant-table-thead')[0]
.getElementById(id)!
.getElementsByClassName('ant-table-thead')[0]
: null;
} else {
tHeader = document.querySelector('.ant-table-wrapper');
@ -269,3 +269,61 @@ export function depthFirstSearch<
console.log(keys);
return c;
}
export function parseHeaders(headers: string) {
if (!headers) return {};
const parsed: any = {};
let key;
let val;
let i;
headers && headers.split('\n').forEach(function parser(line) {
i = line.indexOf(':');
key = line.substring(0, i).trim().toLowerCase();
val = line.substring(i + 1).trim();
if (!key) {
return;
}
parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
});
return parsed;
};
export function parseBody(body: string, contentType: 'application/json' | 'multipart/form-data' | 'application/x-www-form-urlencoded') {
if (!body) return '';
const parsed: any = {};
let key;
let val;
let i;
body && body.split('\n').forEach(function parser(line) {
i = line.indexOf(':');
key = line.substring(0, i).trim().toLowerCase();
val = line.substring(i + 1).trim();
if (!key || parsed[key]) {
return;
}
parsed[key] = val;
});
switch (contentType) {
case 'multipart/form-data':
return Object.keys(parsed).reduce((p, c) => {
p.append(c, parsed[c])
return p;
}, new FormData());
case 'application/x-www-form-urlencoded':
return Object.keys(parsed).reduce((p, c) => {
return p ? `${p}&${c}=${parsed[c]}` : `${c}=${parsed[c]}`;
});
}
return parsed;
};