implement webhook

This commit is contained in:
LouisLam 2021-07-09 19:33:22 +08:00
parent d24a15410e
commit 0ad04d1468
6 changed files with 126 additions and 47 deletions

27
package-lock.json generated
View file

@ -278,8 +278,7 @@
"asynckit": { "asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
"optional": true
}, },
"atob": { "atob": {
"version": "2.1.2", "version": "2.1.2",
@ -575,7 +574,6 @@
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"optional": true,
"requires": { "requires": {
"delayed-stream": "~1.0.0" "delayed-stream": "~1.0.0"
} }
@ -740,8 +738,7 @@
"delayed-stream": { "delayed-stream": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
"optional": true
}, },
"delegates": { "delegates": {
"version": "1.0.0", "version": "1.0.0",
@ -1170,13 +1167,12 @@
"optional": true "optional": true
}, },
"form-data": { "form-data": {
"version": "2.3.3", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"optional": true,
"requires": { "requires": {
"asynckit": "^0.4.0", "asynckit": "^0.4.0",
"combined-stream": "^1.0.6", "combined-stream": "^1.0.8",
"mime-types": "^2.1.12" "mime-types": "^2.1.12"
} }
}, },
@ -2760,6 +2756,17 @@
"uuid": "^3.3.2" "uuid": "^3.3.2"
}, },
"dependencies": { "dependencies": {
"form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"optional": true,
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
}
},
"qs": { "qs": {
"version": "6.5.2", "version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",

View file

@ -14,6 +14,7 @@
"bootstrap": "^5.0.0", "bootstrap": "^5.0.0",
"dayjs": "^1.10.4", "dayjs": "^1.10.4",
"express": "^4.17.1", "express": "^4.17.1",
"form-data": "^4.0.0",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"password-hash": "^1.2.2", "password-hash": "^1.2.2",
"redbean-node": "0.0.20", "redbean-node": "0.0.20",

View file

@ -109,7 +109,7 @@ class Monitor extends BeanModel {
if (! previousBeat || previousBeat.status !== bean.status) { if (! previousBeat || previousBeat.status !== bean.status) {
bean.important = true; bean.important = true;
let notificationList = await R.getAll(`SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? `, [ let notificationList = await R.getAll(`SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id `, [
this.id this.id
]) ])
@ -125,7 +125,7 @@ class Monitor extends BeanModel {
let msg = `[${this.name}] [${text}] ${bean.msg}`; let msg = `[${this.name}] [${text}] ${bean.msg}`;
for(let notification of notificationList) { for(let notification of notificationList) {
promiseList.push(Notification.send(JSON.parse(notification.config), msg)); promiseList.push(Notification.send(JSON.parse(notification.config), msg, await this.toJSON(), bean.toJSON()));
} }
await Promise.all(promiseList); await Promise.all(promiseList);

View file

@ -1,16 +1,56 @@
const axios = require("axios"); const axios = require("axios");
const {R} = require("redbean-node"); const {R} = require("redbean-node");
const FormData = require('form-data');
class Notification { class Notification {
static async send(notification, msg) { static async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
if (notification.type === "telegram") { if (notification.type === "telegram") {
let res = await axios.get(`https://api.telegram.org/bot${notification.telegramBotToken}/sendMessage`, { try {
params: { await axios.get(`https://api.telegram.org/bot${notification.telegramBotToken}/sendMessage`, {
chat_id: notification.telegramChatID, params: {
text: msg, chat_id: notification.telegramChatID,
text: msg,
}
})
return true;
} catch (error) {
console.log(error)
return false;
}
} else if (notification.type === "webhook") {
try {
let data = {
heartbeat: heartbeatJSON,
monitor: monitorJSON,
msg,
};
let finalData;
let config = {};
if (notification.webhookContentType === "form-data") {
finalData = new FormData();
finalData.append('data', JSON.stringify(data));
config = {
headers: finalData.getHeaders()
}
} else {
finalData = data;
} }
})
return true; let res = await axios.post(notification.webhookURL, finalData, config)
console.log(res.data)
return true;
} catch (error) {
console.log(error)
return false;
}
} else { } else {
throw new Error("Notification type is not supported") throw new Error("Notification type is not supported")
} }

View file

@ -12,6 +12,8 @@ const Monitor = require("./model/monitor");
const {getSettings} = require("./util-server"); const {getSettings} = require("./util-server");
const {Notification} = require("./notification") const {Notification} = require("./notification")
app.use(express.json())
let totalClient = 0; let totalClient = 0;
let jwtSecret = null; let jwtSecret = null;
let monitorList = {}; let monitorList = {};
@ -27,6 +29,13 @@ let monitorList = {};
app.use('/', express.static("dist")); app.use('/', express.static("dist"));
app.post('/test-webhook', function(request, response, next) {
console.log("Test Webhook (application/json only)")
console.log("Content-Type: " + request.header("Content-Type"))
console.log(request.body)
response.end();
});
app.get('*', function(request, response, next) { app.get('*', function(request, response, next) {
response.sendFile(process.cwd() + '/dist/index.html'); response.sendFile(process.cwd() + '/dist/index.html');
}); });

View file

@ -10,7 +10,6 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="mb-3"> <div class="mb-3">
<label for="type" class="form-label">Notification Type</label> <label for="type" class="form-label">Notification Type</label>
<select class="form-select" id="type" v-model="notification.type"> <select class="form-select" id="type" v-model="notification.type">
@ -26,39 +25,62 @@
<input type="text" class="form-control" id="name" required v-model="notification.name"> <input type="text" class="form-control" id="name" required v-model="notification.name">
</div> </div>
<div class="mb-3" v-if="notification.type === 'telegram'"> <template v-if="notification.type === 'telegram'">
<label for="telegram-bot-token" class="form-label">Bot Token</label> <div class="mb-3">
<input type="text" class="form-control" id="telegram-bot-token" required v-model="notification.telegramBotToken"> <label for="telegram-bot-token" class="form-label">Bot Token</label>
<div class="form-text">You can get a token from <a href="https://t.me/BotFather" target="_blank">https://t.me/BotFather</a>.</div> <input type="text" class="form-control" id="telegram-bot-token" required v-model="notification.telegramBotToken">
<div class="form-text">You can get a token from <a href="https://t.me/BotFather" target="_blank">https://t.me/BotFather</a>.</div>
</div>
<div class="mb-3">
<label for="telegram-chat-id" class="form-label">Chat ID</label>
<div class="input-group mb-3">
<input type="text" class="form-control" id="telegram-chat-id" required v-model="notification.telegramChatID">
<button class="btn btn-outline-secondary" type="button" @click="autoGetTelegramChatID" v-if="notification.telegramBotToken">Auto Get</button>
</div>
<div class="form-text">
Support Direct Chat / Group / Channel's Chat ID
<p style="margin-top: 8px;">
You can get your chat id by sending message to the bot and go to this url to view the chat_id:
</p>
<p style="margin-top: 8px;">
<template v-if="notification.telegramBotToken">
<a :href="telegramGetUpdatesURL" target="_blank">{{ telegramGetUpdatesURL }}</a>
</template>
<template v-else>
{{ telegramGetUpdatesURL }}
</template>
</p>
</div>
</div>
</template>
<template v-if="notification.type === 'webhook'">
<div class="mb-3">
<label for="webhook-url" class="form-label">Post URL</label>
<input type="url" pattern="https?://.+" class="form-control" id="webhook-url" required v-model="notification.webhookURL">
</div> </div>
<div class="mb-3" v-if="notification.type === 'telegram'"> <div class="mb-3">
<label for="telegram-chat-id" class="form-label">Chat ID</label> <label for="webhook-content-type" class="form-label">Content Type</label>
<select class="form-select" id="webhook-content-type" v-model="notification.webhookContentType" required>
<div class="input-group mb-3"> <option value="json">application/json</option>
<input type="text" class="form-control" id="telegram-chat-id" required v-model="notification.telegramChatID"> <option value="form-data">multipart/form-data</option>
<button class="btn btn-outline-secondary" type="button" @click="autoGetTelegramChatID" v-if="notification.telegramBotToken">Auto Get</button> </select>
</div>
<div class="form-text"> <div class="form-text">
Support Direct Chat / Group / Channel's Chat ID <p>"application/json" is good for any modern http servers such as express.js</p>
<p>"multipart/form-data" is good for PHP, you just need to parse the json by <strong>json_decode($_POST['data'])</strong></p>
<p style="margin-top: 8px;">
You can get your chat id by sending message to the bot and go to this url to view the chat_id:
</p>
<p style="margin-top: 8px;">
<template v-if="notification.telegramBotToken">
<a :href="telegramGetUpdatesURL" target="_blank">{{ telegramGetUpdatesURL }}</a>
</template>
<template v-else>
{{ telegramGetUpdatesURL }}
</template>
</p>
</div> </div>
</div> </div>
</template>
</div> </div>