Merge pull request #3088 from chakflying/feat/webhook-custom-body
Feat: Custom request body for Webhook Notifications
This commit is contained in:
commit
cfb4bbc6cb
|
@ -41,6 +41,7 @@
|
||||||
"jsonwebtoken": "~9.0.0",
|
"jsonwebtoken": "~9.0.0",
|
||||||
"jwt-decode": "~3.1.2",
|
"jwt-decode": "~3.1.2",
|
||||||
"limiter": "~2.1.0",
|
"limiter": "~2.1.0",
|
||||||
|
"liquidjs": "^10.7.0",
|
||||||
"mongodb": "~4.14.0",
|
"mongodb": "~4.14.0",
|
||||||
"mqtt": "~4.3.7",
|
"mqtt": "~4.3.7",
|
||||||
"mssql": "~8.1.4",
|
"mssql": "~8.1.4",
|
||||||
|
@ -13139,6 +13140,33 @@
|
||||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/liquidjs": {
|
||||||
|
"version": "10.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/liquidjs/-/liquidjs-10.7.1.tgz",
|
||||||
|
"integrity": "sha512-tl9nWBZrrKcC61yfih3lbtSjAn+k7e0HhwydPjQKI4+metLk927HYBfXfbf6yrCcYjnBnLzk8xMjUF83yknAQQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"commander": "^10.0.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"liquid": "bin/liquid.js",
|
||||||
|
"liquidjs": "bin/liquid.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/liquidjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/liquidjs/node_modules/commander": {
|
||||||
|
"version": "10.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
|
||||||
|
"integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/listr2": {
|
"node_modules/listr2": {
|
||||||
"version": "3.14.0",
|
"version": "3.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz",
|
||||||
|
|
|
@ -100,6 +100,7 @@
|
||||||
"jsonwebtoken": "~9.0.0",
|
"jsonwebtoken": "~9.0.0",
|
||||||
"jwt-decode": "~3.1.2",
|
"jwt-decode": "~3.1.2",
|
||||||
"limiter": "~2.1.0",
|
"limiter": "~2.1.0",
|
||||||
|
"liquidjs": "^10.7.0",
|
||||||
"mongodb": "~4.14.0",
|
"mongodb": "~4.14.0",
|
||||||
"mqtt": "~4.3.7",
|
"mqtt": "~4.3.7",
|
||||||
"mssql": "~8.1.4",
|
"mssql": "~8.1.4",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const NotificationProvider = require("./notification-provider");
|
const NotificationProvider = require("./notification-provider");
|
||||||
const axios = require("axios");
|
const axios = require("axios");
|
||||||
const FormData = require("form-data");
|
const FormData = require("form-data");
|
||||||
|
const { Liquid } = require("liquidjs");
|
||||||
|
|
||||||
class Webhook extends NotificationProvider {
|
class Webhook extends NotificationProvider {
|
||||||
|
|
||||||
|
@ -15,17 +16,27 @@ class Webhook extends NotificationProvider {
|
||||||
monitor: monitorJSON,
|
monitor: monitorJSON,
|
||||||
msg,
|
msg,
|
||||||
};
|
};
|
||||||
let finalData;
|
|
||||||
let config = {
|
let config = {
|
||||||
headers: {}
|
headers: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (notification.webhookContentType === "form-data") {
|
if (notification.webhookContentType === "form-data") {
|
||||||
finalData = new FormData();
|
const formData = new FormData();
|
||||||
finalData.append("data", JSON.stringify(data));
|
formData.append("data", JSON.stringify(data));
|
||||||
config.headers = finalData.getHeaders();
|
config.headers = formData.getHeaders();
|
||||||
} else {
|
data = formData;
|
||||||
finalData = data;
|
} else if (notification.webhookContentType === "custom") {
|
||||||
|
// Initialize LiquidJS and parse the custom Body Template
|
||||||
|
const engine = new Liquid();
|
||||||
|
const tpl = engine.parse(notification.webhookCustomBody);
|
||||||
|
|
||||||
|
// Insert templated values into Body
|
||||||
|
data = await engine.render(tpl,
|
||||||
|
{
|
||||||
|
msg,
|
||||||
|
heartbeatJSON,
|
||||||
|
monitorJSON
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notification.webhookAdditionalHeaders) {
|
if (notification.webhookAdditionalHeaders) {
|
||||||
|
@ -39,7 +50,7 @@ class Webhook extends NotificationProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await axios.post(notification.webhookURL, finalData, config);
|
await axios.post(notification.webhookURL, data, config);
|
||||||
return okMsg;
|
return okMsg;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -208,6 +208,7 @@ let needSetup = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isDev) {
|
if (isDev) {
|
||||||
|
app.use(express.urlencoded({ extended: true }));
|
||||||
app.post("/test-webhook", async (request, response) => {
|
app.post("/test-webhook", async (request, response) => {
|
||||||
log.debug("test", request.headers);
|
log.debug("test", request.headers);
|
||||||
log.debug("test", request.body);
|
log.debug("test", request.body);
|
||||||
|
|
|
@ -12,61 +12,97 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="webhook-content-type" class="form-label">{{
|
<label for="webhook-request-body" class="form-label">{{
|
||||||
$t("Content Type")
|
$t("Request Body")
|
||||||
}}</label>
|
}}</label>
|
||||||
<select
|
<select
|
||||||
id="webhook-content-type"
|
id="webhook-request-body"
|
||||||
v-model="$parent.notification.webhookContentType"
|
v-model="$parent.notification.webhookContentType"
|
||||||
class="form-select"
|
class="form-select"
|
||||||
required
|
required
|
||||||
>
|
>
|
||||||
<option value="json">application/json</option>
|
<option value="json">{{ $t("webhookBodyPresetOption", ["application/json"]) }}</option>
|
||||||
<option value="form-data">multipart/form-data</option>
|
<option value="form-data">{{ $t("webhookBodyPresetOption", ["multipart/form-data"]) }}</option>
|
||||||
|
<option value="custom">{{ $t("webhookBodyCustomOption") }}</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<div class="form-text">
|
<div class="form-text">
|
||||||
<p>{{ $t("webhookJsonDesc", ['"application/json"']) }}</p>
|
<div v-if="$parent.notification.webhookContentType == 'json'">
|
||||||
<i18n-t tag="p" keypath="webhookFormDataDesc">
|
<p>{{ $t("webhookJsonDesc", ['"application/json"']) }}</p>
|
||||||
<template #multipart>"multipart/form-data"</template>
|
</div>
|
||||||
<template #decodeFunction>
|
<div v-if="$parent.notification.webhookContentType == 'form-data'">
|
||||||
<strong>json_decode($_POST['data'])</strong>
|
<i18n-t tag="p" keypath="webhookFormDataDesc">
|
||||||
</template>
|
<template #multipart>multipart/form-data"</template>
|
||||||
</i18n-t>
|
<template #decodeFunction>
|
||||||
|
<strong>json_decode($_POST['data'])</strong>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
<div v-if="$parent.notification.webhookContentType == 'custom'">
|
||||||
|
<i18n-t tag="p" keypath="webhookCustomBodyDesc">
|
||||||
|
<template #msg>
|
||||||
|
<code>msg</code>
|
||||||
|
</template>
|
||||||
|
<template #heartbeat>
|
||||||
|
<code>heartbeatJSON</code>
|
||||||
|
</template>
|
||||||
|
<template #monitor>
|
||||||
|
<code>monitorJSON</code>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
v-if="$parent.notification.webhookContentType == 'custom'"
|
||||||
|
id="customBody"
|
||||||
|
v-model="$parent.notification.webhookCustomBody"
|
||||||
|
class="form-control"
|
||||||
|
:placeholder="customBodyPlaceholder"
|
||||||
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<i18n-t
|
<div class="form-check form-switch">
|
||||||
tag="label"
|
<input v-model="showAdditionalHeadersField" class="form-check-input" type="checkbox">
|
||||||
class="form-label"
|
<label class="form-check-label">{{ $t("webhookAdditionalHeadersTitle") }}</label>
|
||||||
for="additionalHeaders"
|
</div>
|
||||||
keypath="webhookAdditionalHeadersTitle"
|
<div class="form-text">
|
||||||
>
|
<i18n-t tag="p" keypath="webhookAdditionalHeadersDesc"> </i18n-t>
|
||||||
</i18n-t>
|
</div>
|
||||||
<textarea
|
<textarea
|
||||||
|
v-if="showAdditionalHeadersField"
|
||||||
id="additionalHeaders"
|
id="additionalHeaders"
|
||||||
v-model="$parent.notification.webhookAdditionalHeaders"
|
v-model="$parent.notification.webhookAdditionalHeaders"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:placeholder="headersPlaceholder"
|
:placeholder="headersPlaceholder"
|
||||||
></textarea>
|
></textarea>
|
||||||
<div class="form-text">
|
|
||||||
<i18n-t tag="p" keypath="webhookAdditionalHeadersDesc"> </i18n-t>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showAdditionalHeadersField: this.$parent.notification.webhookAdditionalHeaders != null,
|
||||||
|
};
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
headersPlaceholder() {
|
headersPlaceholder() {
|
||||||
return this.$t("Example:", [
|
return this.$t("Example:", [
|
||||||
`
|
`
|
||||||
{
|
{
|
||||||
"HeaderName": "HeaderValue"
|
"Authorization": "Authorization Token"
|
||||||
}`,
|
}`,
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
customBodyPlaceholder() {
|
||||||
|
return `Example:
|
||||||
|
{
|
||||||
|
"Title": "Uptime Kuma Alert - {{ monitorJSON['name'] }}",
|
||||||
|
"Body": "{{ msg }}"
|
||||||
|
}`;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -196,8 +196,11 @@
|
||||||
"Content Type": "Content Type",
|
"Content Type": "Content Type",
|
||||||
"webhookJsonDesc": "{0} is good for any modern HTTP servers such as Express.js",
|
"webhookJsonDesc": "{0} is good for any modern HTTP servers such as Express.js",
|
||||||
"webhookFormDataDesc": "{multipart} is good for PHP. The JSON will need to be parsed with {decodeFunction}",
|
"webhookFormDataDesc": "{multipart} is good for PHP. The JSON will need to be parsed with {decodeFunction}",
|
||||||
|
"webhookCustomBodyDesc": "Define a custom HTTP Body for the request. Template variables {msg}, {heartbeat}, {monitor} are accepted.",
|
||||||
"webhookAdditionalHeadersTitle": "Additional Headers",
|
"webhookAdditionalHeadersTitle": "Additional Headers",
|
||||||
"webhookAdditionalHeadersDesc": "Sets additional headers sent with the webhook.",
|
"webhookAdditionalHeadersDesc": "Sets additional headers sent with the webhook. Each header should be defined as a JSON key/value.",
|
||||||
|
"webhookBodyPresetOption": "Preset - {0}",
|
||||||
|
"webhookBodyCustomOption": "Custom Body",
|
||||||
"Webhook URL": "Webhook URL",
|
"Webhook URL": "Webhook URL",
|
||||||
"Application Token": "Application Token",
|
"Application Token": "Application Token",
|
||||||
"Server URL": "Server URL",
|
"Server URL": "Server URL",
|
||||||
|
@ -757,5 +760,6 @@
|
||||||
"Group": "Group",
|
"Group": "Group",
|
||||||
"Monitor Group": "Monitor Group",
|
"Monitor Group": "Monitor Group",
|
||||||
"noGroupMonitorMsg": "Not Available. Create a Group Monitor First.",
|
"noGroupMonitorMsg": "Not Available. Create a Group Monitor First.",
|
||||||
"Close": "Close"
|
"Close": "Close",
|
||||||
|
"Request Body": "Request Body"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue