修复自定义通知

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 webhookBody: any = {};
public webhookUrl: string = ''; public webhookUrl: string = '';
public webhookMethod: 'GET' | 'POST' | 'PUT' = 'GET'; public webhookMethod: 'GET' | 'POST' | 'PUT' = 'GET';
public webhookContentType: 'application/json' | 'multipart/form-data' | 'application/x-www-form-urlencoded' = 'application/json';
} }
export interface NotificationInfo export interface NotificationInfo

View File

@ -34,7 +34,7 @@ export default class NotificationService {
private content = ''; private content = '';
private params!: Omit<NotificationInfo, 'type'>; private params!: Omit<NotificationInfo, 'type'>;
constructor(@Inject('logger') private logger: winston.Logger) {} constructor(@Inject('logger') private logger: winston.Logger) { }
public async notify( public async notify(
title: string, title: string,
@ -181,9 +181,8 @@ export default class NotificationService {
telegramBotUserId, telegramBotUserId,
} = this.params; } = this.params;
const authStr = telegramBotProxyAuth ? `${telegramBotProxyAuth}@` : ''; const authStr = telegramBotProxyAuth ? `${telegramBotProxyAuth}@` : '';
const url = `https://${ const url = `https://${telegramBotApiHost ? telegramBotApiHost : 'api.telegram.org'
telegramBotApiHost ? telegramBotApiHost : 'api.telegram.org' }/bot${telegramBotToken}/sendMessage`;
}/bot${telegramBotToken}/sendMessage`;
let agent; let agent;
if (telegramBotProxyHost && telegramBotProxyPort) { if (telegramBotProxyHost && telegramBotProxyPort) {
const options: any = { const options: any = {
@ -386,17 +385,32 @@ export default class NotificationService {
} }
private async webhook() { private async webhook() {
const { webhookUrl, webhookBody, webhookHeaders, webhookMethod } = const { webhookUrl, webhookBody, webhookHeaders, webhookMethod, webhookContentType } =
this.params; this.params;
const { statusCode } = await got(webhookUrl, { const bodyParam = this.formatBody(webhookContentType, webhookBody);
const options = {
method: webhookMethod, method: webhookMethod,
headers: webhookHeaders, headers: webhookHeaders,
body: webhookBody,
timeout: this.timeout, timeout: this.timeout,
retry: 0, 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 { Typography, Input, Form, Button, Select, message } from 'antd';
import { request } from '@/utils/http'; import { request } from '@/utils/http';
import config from '@/utils/config'; import config from '@/utils/config';
import { parseBody, parseHeaders } from '@/utils';
const { Option } = Select; const { Option } = Select;
@ -12,9 +13,15 @@ const NotificationSetting = ({ data }: any) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const handleOk = (values: any) => { const handleOk = (values: any) => {
if (values.type == 'closed') { const { type, webhookBody, webhookContentType } = values;
if (type == 'closed') {
values.type = ''; values.type = '';
} }
if (type === 'webhook') {
values.webhookHeaders = { ...parseHeaders(values.webhookHeaders) };
values.webhookBody = parseBody(webhookBody, webhookContentType);
}
request request
.put(`${config.apiPrefix}user/notification`, { .put(`${config.apiPrefix}user/notification`, {
data: { data: {
@ -73,10 +80,22 @@ const NotificationSetting = ({ data }: any) => {
rules={[{ required: x.required }]} rules={[{ required: x.required }]}
style={{ maxWidth: 400 }} style={{ maxWidth: 400 }}
> >
<Input.TextArea {
autoSize={true} x.items ? (
placeholder={x.placeholder || `请输入${x.label}`} <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> </Form.Item>
))} ))}
<Button type="primary" htmlType="submit"> <Button type="primary" htmlType="submit">

View File

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

View File

@ -150,9 +150,9 @@ export default function browserType() {
shell === 'none' shell === 'none'
? {} ? {}
: { : {
shell, // wechat qq uc 360 2345 sougou liebao maxthon shell, // wechat qq uc 360 2345 sougou liebao maxthon
shellVs, shellVs,
}, },
); );
console.log( console.log(
@ -188,8 +188,8 @@ export function getTableScroll({
if (id) { if (id) {
tHeader = document.getElementById(id) tHeader = document.getElementById(id)
? document ? document
.getElementById(id)! .getElementById(id)!
.getElementsByClassName('ant-table-thead')[0] .getElementsByClassName('ant-table-thead')[0]
: null; : null;
} else { } else {
tHeader = document.querySelector('.ant-table-wrapper'); tHeader = document.querySelector('.ant-table-wrapper');
@ -269,3 +269,61 @@ export function depthFirstSearch<
console.log(keys); console.log(keys);
return c; 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;
};