mirror of https://github.com/louislam/uptime-kuma
				
				
				
			
		
			
				
	
	
		
			498 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			JavaScript
		
	
	
			
		
		
	
	
			498 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			JavaScript
		
	
	
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 {
 | 
						|
                const discordDisplayName = notification.discordUsername || "Uptime Kuma";
 | 
						|
 | 
						|
                // If heartbeatJSON is null, assume we're testing.
 | 
						|
                if (heartbeatJSON == null) {
 | 
						|
                    let discordtestdata = {
 | 
						|
                        username: discordDisplayName,
 | 
						|
                        content: msg,
 | 
						|
                    }
 | 
						|
                    await axios.post(notification.discordWebhookUrl, discordtestdata)
 | 
						|
                    return okMsg;
 | 
						|
                }
 | 
						|
                // If heartbeatJSON is not null, we go into the normal alerting loop.
 | 
						|
                if (heartbeatJSON["status"] == 0) {
 | 
						|
                    let discorddowndata = {
 | 
						|
                        username: discordDisplayName,
 | 
						|
                        embeds: [{
 | 
						|
                            title: "â One of your services went down. â",
 | 
						|
                            color: 16711680,
 | 
						|
                            timestamp: heartbeatJSON["time"],
 | 
						|
                            fields: [
 | 
						|
                                {
 | 
						|
                                    name: "Service Name",
 | 
						|
                                    value: monitorJSON["name"],
 | 
						|
                                },
 | 
						|
                                {
 | 
						|
                                    name: "Service URL",
 | 
						|
                                    value: monitorJSON["url"],
 | 
						|
                                },
 | 
						|
                                {
 | 
						|
                                    name: "Time (UTC)",
 | 
						|
                                    value: heartbeatJSON["time"],
 | 
						|
                                },
 | 
						|
                                {
 | 
						|
                                    name: "Error",
 | 
						|
                                    value: heartbeatJSON["msg"],
 | 
						|
                                },
 | 
						|
                            ],
 | 
						|
                        }],
 | 
						|
                    }
 | 
						|
                    await axios.post(notification.discordWebhookUrl, discorddowndata)
 | 
						|
                    return okMsg;
 | 
						|
 | 
						|
                } else if (heartbeatJSON["status"] == 1) {
 | 
						|
                    let discordupdata = {
 | 
						|
                        username: discordDisplayName,
 | 
						|
                        embeds: [{
 | 
						|
                            title: "â
 Your service " + monitorJSON["name"] + " is up! â
",
 | 
						|
                            color: 65280,
 | 
						|
                            timestamp: heartbeatJSON["time"],
 | 
						|
                            fields: [
 | 
						|
                                {
 | 
						|
                                    name: "Service Name",
 | 
						|
                                    value: monitorJSON["name"],
 | 
						|
                                },
 | 
						|
                                {
 | 
						|
                                    name: "Service URL",
 | 
						|
                                    value: "[Visit Service](" + monitorJSON["url"] + ")",
 | 
						|
                                },
 | 
						|
                                {
 | 
						|
                                    name: "Time (UTC)",
 | 
						|
                                    value: heartbeatJSON["time"],
 | 
						|
                                },
 | 
						|
                                {
 | 
						|
                                    name: "Ping",
 | 
						|
                                    value: heartbeatJSON["ping"] + "ms",
 | 
						|
                                },
 | 
						|
                            ],
 | 
						|
                        }],
 | 
						|
                    }
 | 
						|
                    await axios.post(notification.discordWebhookUrl, discordupdata)
 | 
						|
                    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 === "octopush") {
 | 
						|
            try {
 | 
						|
                let config = {
 | 
						|
                    headers: {
 | 
						|
                        'api-key': notification.octopushAPIKey,
 | 
						|
                        'api-login': notification.octopushLogin,
 | 
						|
                        'cache-control': 'no-cache'
 | 
						|
                    }
 | 
						|
                };
 | 
						|
                let data = {
 | 
						|
                    "recipients": [
 | 
						|
                        {
 | 
						|
                            "phone_number": notification.octopushPhoneNumber
 | 
						|
                        }
 | 
						|
                    ],
 | 
						|
                    //octopush not supporting non ascii char
 | 
						|
                    "text": msg.replace(/[^\x00-\x7F]/g, ""),
 | 
						|
                    "type": notification.octopushSMSType,
 | 
						|
                    "purpose": "alert",
 | 
						|
                    "sender": notification.octopushSenderName
 | 
						|
                };
 | 
						|
 | 
						|
                await axios.post(`https://api.octopush.com/v1/public/sms-campaign/send`, data, config)
 | 
						|
                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 if (notification.type === "pushbullet") {
 | 
						|
            try {
 | 
						|
                let pushbulletUrl = `https://api.pushbullet.com/v2/pushes`;
 | 
						|
                let config = {
 | 
						|
                    headers: {
 | 
						|
                        'Access-Token': notification.pushbulletAccessToken,
 | 
						|
                        'Content-Type': 'application/json'
 | 
						|
                    }
 | 
						|
                };
 | 
						|
                if (heartbeatJSON == null) {
 | 
						|
                    let testdata = {
 | 
						|
                        "type": "note", 
 | 
						|
                        "title": "Uptime Kuma Alert",
 | 
						|
                        "body": "Testing Successful.",
 | 
						|
                    }
 | 
						|
                    await axios.post(pushbulletUrl, testdata, config)
 | 
						|
                } else if (heartbeatJSON["status"] == 0) {
 | 
						|
                    let downdata = {
 | 
						|
                        "type": "note", 
 | 
						|
                        "title": "UptimeKuma Alert:" + monitorJSON["name"],
 | 
						|
                        "body": "[đ´ Down]" + heartbeatJSON["msg"] + "\nTime (UTC):" + heartbeatJSON["time"],
 | 
						|
                    }
 | 
						|
                    await axios.post(pushbulletUrl, downdata, config)
 | 
						|
                } else if (heartbeatJSON["status"] == 1) {
 | 
						|
                    let updata = {
 | 
						|
                        "type": "note", 
 | 
						|
                        "title": "UptimeKuma Alert:" + monitorJSON["name"],
 | 
						|
                        "body": "[â
 Up]" + heartbeatJSON["msg"] + "\nTime (UTC):" + heartbeatJSON["time"],
 | 
						|
                    }
 | 
						|
                    await axios.post(pushbulletUrl, updata, config)
 | 
						|
                }
 | 
						|
                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,
 | 
						|
}
 |