|
|
|
const axios = require("axios");
|
|
|
|
const { R } = require("redbean-node");
|
|
|
|
const FormData = require("form-data");
|
|
|
|
const nodemailer = require("nodemailer");
|
|
|
|
const child_process = require("child_process");
|
|
|
|
|
|
|
|
class Notification {
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param notification
|
|
|
|
* @param msg
|
|
|
|
* @param monitorJSON
|
|
|
|
* @param heartbeatJSON
|
|
|
|
* @returns {Promise<string>} Successful msg
|
|
|
|
* Throw Error with fail msg
|
|
|
|
*/
|
|
|
|
static async send(notification, msg, monitorJSON = null, heartbeatJSON = null) {
|
|
|
|
let okMsg = "Sent Successfully. ";
|
|
|
|
|
|
|
|
if (notification.type === "telegram") {
|
|
|
|
try {
|
|
|
|
await axios.get(`https://api.telegram.org/bot${notification.telegramBotToken}/sendMessage`, {
|
|
|
|
params: {
|
|
|
|
chat_id: notification.telegramChatID,
|
|
|
|
text: msg,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
return okMsg;
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
let msg = (error.response.data.description) ? error.response.data.description : "Error without description"
|
|
|
|
throw new Error(msg)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (notification.type === "gotify") {
|
|
|
|
try {
|
|
|
|
if (notification.gotifyserverurl && notification.gotifyserverurl.endsWith("/")) {
|
|
|
|
notification.gotifyserverurl = notification.gotifyserverurl.slice(0, -1);
|
|
|
|
}
|
|
|
|
await axios.post(`${notification.gotifyserverurl}/message?token=${notification.gotifyapplicationToken}`, {
|
|
|
|
"message": msg,
|
|
|
|
"priority": notification.gotifyPriority || 8,
|
|
|
|
"title": "Uptime-Kuma",
|
|
|
|
})
|
|
|
|
|
|
|
|
return okMsg;
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
throwGeneralAxiosError(error)
|
|
|
|
}
|
|
|
|
|
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
await axios.post(notification.webhookURL, finalData, config)
|
|
|
|
return okMsg;
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
throwGeneralAxiosError(error)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (notification.type === "smtp") {
|
|
|
|
return await Notification.smtp(notification, msg)
|
|
|
|
|
|
|
|
} else if (notification.type === "discord") {
|
|
|
|
try {
|
|
|
|
// If heartbeatJSON is null, assume we're testing.
|
|
|
|
if (heartbeatJSON == null) {
|
|
|
|
let data = {
|
|
|
|
username: "Uptime-Kuma",
|
|
|
|
content: msg,
|
|
|
|
}
|
|
|
|
await axios.post(notification.discordWebhookUrl, data)
|
|
|
|
return okMsg;
|
|
|
|
}
|
|
|
|
// If heartbeatJSON is not null, we go into the normal alerting loop.
|
|
|
|
if (heartbeatJSON["status"] == 0) {
|
|
|
|
var alertColor = "16711680";
|
|
|
|
} else if (heartbeatJSON["status"] == 1) {
|
|
|
|
var alertColor = "65280";
|
|
|
|
}
|
|
|
|
let data = {
|
|
|
|
username: "Uptime-Kuma",
|
|
|
|
embeds: [{
|
|
|
|
title: "Uptime-Kuma Alert",
|
|
|
|
color: alertColor,
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
name: "Time (UTC)",
|
|
|
|
value: heartbeatJSON["time"],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "Message",
|
|
|
|
value: msg,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
}],
|
|
|
|
}
|
|
|
|
await axios.post(notification.discordWebhookUrl, data)
|
|
|
|
return okMsg;
|
|
|
|
} catch (error) {
|
|
|
|
throwGeneralAxiosError(error)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (notification.type === "signal") {
|
|
|
|
try {
|
|
|
|
let data = {
|
|
|
|
"message": msg,
|
|
|
|
"number": notification.signalNumber,
|
|
|
|
"recipients": notification.signalRecipients.replace(/\s/g, "").split(","),
|
|
|
|
};
|
|
|
|
let config = {};
|
|
|
|
|
|
|
|
await axios.post(notification.signalURL, data, config)
|
|
|
|
return okMsg;
|
|
|
|
} catch (error) {
|
|
|
|
throwGeneralAxiosError(error)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (notification.type === "pushy") {
|
|
|
|
try {
|
|
|
|
await axios.post(`https://api.pushy.me/push?api_key=${notification.pushyAPIKey}`, {
|
|
|
|
"to": notification.pushyToken,
|
|
|
|
"data": {
|
|
|
|
"message": "Uptime-Kuma"
|
|
|
|
},
|
|
|
|
"notification": {
|
|
|
|
"body": msg,
|
|
|
|
"badge": 1,
|
|
|
|
"sound": "ping.aiff"
|
|
|
|
}
|
|
|
|
})
|
|
|
|
return true;
|
|
|
|
} catch (error) {
|
|
|
|
console.log(error)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (notification.type === "slack") {
|
|
|
|
try {
|
|
|
|
if (heartbeatJSON == null) {
|
|
|
|
let data = {
|
|
|
|
"text": "Uptime Kuma Slack testing successful.",
|
|
|
|
"channel": notification.slackchannel,
|
|
|
|
"username": notification.slackusername,
|
|
|
|
"icon_emoji": notification.slackiconemo,
|
|
|
|
}
|
|
|
|
await axios.post(notification.slackwebhookURL, data)
|
|
|
|
return okMsg;
|
|
|
|
}
|
|
|
|
|
|
|
|
const time = heartbeatJSON["time"];
|
|
|
|
let data = {
|
|
|
|
"text": "Uptime Kuma Alert",
|
|
|
|
"channel": notification.slackchannel,
|
|
|
|
"username": notification.slackusername,
|
|
|
|
"icon_emoji": notification.slackiconemo,
|
|
|
|
"blocks": [{
|
|
|
|
"type": "header",
|
|
|
|
"text": {
|
|
|
|
"type": "plain_text",
|
|
|
|
"text": "Uptime Kuma Alert",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "section",
|
|
|
|
"fields": [{
|
|
|
|
"type": "mrkdwn",
|
|
|
|
"text": "*Message*\n" + msg,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "mrkdwn",
|
|
|
|
"text": "*Time (UTC)*\n" + time,
|
|
|
|
}],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"type": "actions",
|
|
|
|
"elements": [
|
|
|
|
{
|
|
|
|
"type": "button",
|
|
|
|
"text": {
|
|
|
|
"type": "plain_text",
|
|
|
|
"text": "Visit Uptime Kuma",
|
|
|
|
},
|
|
|
|
"value": "Uptime-Kuma",
|
|
|
|
"url": notification.slackbutton || "https://github.com/louislam/uptime-kuma",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
}],
|
|
|
|
}
|
|
|
|
await axios.post(notification.slackwebhookURL, data)
|
|
|
|
return okMsg;
|
|
|
|
} catch (error) {
|
|
|
|
throwGeneralAxiosError(error)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (notification.type === "pushover") {
|
|
|
|
let pushoverlink = "https://api.pushover.net/1/messages.json"
|
|
|
|
try {
|
|
|
|
if (heartbeatJSON == null) {
|
|
|
|
let data = {
|
|
|
|
"message": "<b>Uptime Kuma Pushover testing successful.</b>",
|
|
|
|
"user": notification.pushoveruserkey,
|
|
|
|
"token": notification.pushoverapptoken,
|
|
|
|
"sound": notification.pushoversounds,
|
|
|
|
"priority": notification.pushoverpriority,
|
|
|
|
"title": notification.pushovertitle,
|
|
|
|
"retry": "30",
|
|
|
|
"expire": "3600",
|
|
|
|
"html": 1,
|
|
|
|
}
|
|
|
|
await axios.post(pushoverlink, data)
|
|
|
|
return okMsg;
|
|
|
|
}
|
|
|
|
|
|
|
|
let data = {
|
|
|
|
"message": "<b>Uptime Kuma Alert</b>\n\n<b>Message</b>:" + msg + "\n<b>Time (UTC)</b>:" + heartbeatJSON["time"],
|
|
|
|
"user": notification.pushoveruserkey,
|
|
|
|
"token": notification.pushoverapptoken,
|
|
|
|
"sound": notification.pushoversounds,
|
|
|
|
"priority": notification.pushoverpriority,
|
|
|
|
"title": notification.pushovertitle,
|
|
|
|
"retry": "30",
|
|
|
|
"expire": "3600",
|
|
|
|
"html": 1,
|
|
|
|
}
|
|
|
|
await axios.post(pushoverlink, data)
|
|
|
|
return okMsg;
|
|
|
|
} catch (error) {
|
|
|
|
throwGeneralAxiosError(error)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (notification.type === "apprise") {
|
|
|
|
|
|
|
|
return Notification.apprise(notification, msg)
|
|
|
|
|
|
|
|
} else if (notification.type === "lunasea") {
|
|
|
|
let lunaseadevice = "https://notify.lunasea.app/v1/custom/device/" + notification.lunaseaDevice
|
|
|
|
|
|
|
|
try {
|
|
|
|
if (heartbeatJSON == null) {
|
|
|
|
let testdata = {
|
|
|
|
"title": "Uptime Kuma Alert",
|
|
|
|
"body": "Testing Successful.",
|
|
|
|
}
|
|
|
|
await axios.post(lunaseadevice, testdata)
|
|
|
|
return okMsg;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (heartbeatJSON["status"] == 0) {
|
|
|
|
let downdata = {
|
|
|
|
"title": "UptimeKuma Alert:" + monitorJSON["name"],
|
|
|
|
"body": "[🔴 Down]" + heartbeatJSON["msg"] + "\nTime (UTC):" + heartbeatJSON["time"],
|
|
|
|
}
|
|
|
|
await axios.post(lunaseadevice, downdata)
|
|
|
|
return okMsg;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (heartbeatJSON["status"] == 1) {
|
|
|
|
let updata = {
|
|
|
|
"title": "UptimeKuma Alert:" + monitorJSON["name"],
|
|
|
|
"body": "[✅ Up]" + heartbeatJSON["msg"] + "\nTime (UTC):" + heartbeatJSON["time"],
|
|
|
|
}
|
|
|
|
await axios.post(lunaseadevice, updata)
|
|
|
|
return okMsg;
|
|
|
|
}
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
throwGeneralAxiosError(error)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
throw new Error("Notification type is not supported")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static async save(notification, notificationID, userID) {
|
|
|
|
let bean
|
|
|
|
|
|
|
|
if (notificationID) {
|
|
|
|
bean = await R.findOne("notification", " id = ? AND user_id = ? ", [
|
|
|
|
notificationID,
|
|
|
|
userID,
|
|
|
|
])
|
|
|
|
|
|
|
|
if (! bean) {
|
|
|
|
throw new Error("notification not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
bean = R.dispense("notification")
|
|
|
|
}
|
|
|
|
|
|
|
|
bean.name = notification.name;
|
|
|
|
bean.user_id = userID;
|
|
|
|
bean.config = JSON.stringify(notification)
|
|
|
|
await R.store(bean)
|
|
|
|
}
|
|
|
|
|
|
|
|
static async delete(notificationID, userID) {
|
|
|
|
let bean = await R.findOne("notification", " id = ? AND user_id = ? ", [
|
|
|
|
notificationID,
|
|
|
|
userID,
|
|
|
|
])
|
|
|
|
|
|
|
|
if (! bean) {
|
|
|
|
throw new Error("notification not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
await R.trash(bean)
|
|
|
|
}
|
|
|
|
|
|
|
|
static async smtp(notification, msg) {
|
|
|
|
|
|
|
|
let transporter = nodemailer.createTransport({
|
|
|
|
host: notification.smtpHost,
|
|
|
|
port: notification.smtpPort,
|
|
|
|
secure: notification.smtpSecure,
|
|
|
|
auth: {
|
|
|
|
user: notification.smtpUsername,
|
|
|
|
pass: notification.smtpPassword,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
// send mail with defined transport object
|
|
|
|
await transporter.sendMail({
|
|
|
|
from: `"Uptime Kuma" <${notification.smtpFrom}>`,
|
|
|
|
to: notification.smtpTo,
|
|
|
|
subject: msg,
|
|
|
|
text: msg,
|
|
|
|
});
|
|
|
|
|
|
|
|
return "Sent Successfully.";
|
|
|
|
}
|
|
|
|
|
|
|
|
static async apprise(notification, msg) {
|
|
|
|
let s = child_process.spawnSync("apprise", [ "-vv", "-b", msg, notification.appriseURL])
|
|
|
|
|
|
|
|
let output = (s.stdout) ? s.stdout.toString() : "ERROR: maybe apprise not found";
|
|
|
|
|
|
|
|
if (output) {
|
|
|
|
|
|
|
|
if (! output.includes("ERROR")) {
|
|
|
|
return "Sent Successfully";
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new Error(output)
|
|
|
|
} else {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static checkApprise() {
|
|
|
|
let commandExistsSync = require("command-exists").sync;
|
|
|
|
let exists = commandExistsSync("apprise");
|
|
|
|
return exists;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function throwGeneralAxiosError(error) {
|
|
|
|
let msg = "Error: " + error + " ";
|
|
|
|
|
|
|
|
if (error.response && error.response.data) {
|
|
|
|
if (typeof error.response.data === "string") {
|
|
|
|
msg += error.response.data;
|
|
|
|
} else {
|
|
|
|
msg += JSON.stringify(error.response.data)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
throw new Error(msg)
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
Notification,
|
|
|
|
}
|