hacktoberfestdockersingle-page-appselfhostedself-hostedresponsivemonitoringwebsocketwebappuptime-monitoringuptimesocket-io
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
292 lines
9.6 KiB
292 lines
9.6 KiB
const { R } = require("redbean-node");
|
|
const { Liquid } = require( 'liquidjs');
|
|
const { UP } = require("../src/util");
|
|
const dayjs = require("dayjs");
|
|
|
|
const engine = new Liquid();
|
|
|
|
const Apprise = require("./notification-providers/apprise");
|
|
const Discord = require("./notification-providers/discord");
|
|
const Gotify = require("./notification-providers/gotify");
|
|
const Line = require("./notification-providers/line");
|
|
const LunaSea = require("./notification-providers/lunasea");
|
|
const Mattermost = require("./notification-providers/mattermost");
|
|
const Matrix = require("./notification-providers/matrix");
|
|
const Octopush = require("./notification-providers/octopush");
|
|
const PromoSMS = require("./notification-providers/promosms");
|
|
const Pushbullet = require("./notification-providers/pushbullet");
|
|
const Pushover = require("./notification-providers/pushover");
|
|
const Pushy = require("./notification-providers/pushy");
|
|
const RocketChat = require("./notification-providers/rocket-chat");
|
|
const Signal = require("./notification-providers/signal");
|
|
const Slack = require("./notification-providers/slack");
|
|
const SMTP = require("./notification-providers/smtp");
|
|
const Teams = require("./notification-providers/teams");
|
|
const Telegram = require("./notification-providers/telegram");
|
|
const Webhook = require("./notification-providers/webhook");
|
|
const Feishu = require("./notification-providers/feishu");
|
|
const AliyunSms = require("./notification-providers/aliyun-sms");
|
|
const DingDing = require("./notification-providers/dingding");
|
|
|
|
|
|
const MinimalDetailTemplate = "{{monitor.name}}: {{monitor.health}}"
|
|
const LowDetailTemplate = "[{{monitor.name}}] [{{monitor.health}}] {{heartbeat.msg}}"
|
|
const MediumDetailTemplate = `Monitor: {{monitor.name}}
|
|
Health: {{monitor.health}}
|
|
Address: {{monitor.url}}
|
|
|
|
{% if heartbeat.status == 1 and monitor.upsideDown -%}
|
|
Your Upside down {{monitor.type}} monitor is unexpectedly connected
|
|
{%- elsif heartbeat.status == 1 and monitor.upsideDown==false -%}
|
|
Your {{monitor.type}} monitor is up
|
|
{%- elsif heartbeat.status == 0 and monitor.upsideDown -%}
|
|
Your Upside down {{monitor.type}} monitor is no longer connected
|
|
{%- elsif heartbeat.status == 0 and monitor.upsideDown == false -%}
|
|
Your {{monitor.type}} monitor is unexpectedly down.
|
|
{%- endif %}
|
|
Time: {{heartbeat.time}}
|
|
Uptime Message: {{heartbeat.msg}}`
|
|
|
|
const FullDetailTemplate = `Monitor: {{monitor.name}}
|
|
Health: {{monitor.health}}
|
|
Address: {{monitor.url}}
|
|
|
|
{% if heartbeat.status == 1 and monitor.upsideDown -%}
|
|
Your Upside down {{monitor.type}} monitor is unexpectedly connected
|
|
{%- elsif heartbeat.status == 1 and monitor.upsideDown==false -%}
|
|
Your {{monitor.type}} monitor is up
|
|
{%- elsif heartbeat.status == 0 and monitor.upsideDown -%}
|
|
Your Upside down {{monitor.type}} monitor is no longer connected
|
|
{%- elsif heartbeat.status == 0 and monitor.upsideDown == false -%}
|
|
Your {{monitor.type}} monitor is unexpectedly down.
|
|
{%- endif %}
|
|
Time: {{heartbeat.time}}
|
|
Uptime Message: {{heartbeat.msg}}
|
|
|
|
Tags
|
|
----------------------------------------
|
|
{% for tag in monitor.tags -%}
|
|
{{ tag.name }}
|
|
{%- if tag.value and tag.value != "" -%}
|
|
: {{tag.value}}
|
|
{%- endif %}
|
|
{% endfor -%}`
|
|
|
|
class Notification {
|
|
|
|
static generateTestHeartbeat(){
|
|
return {
|
|
monitorID: 5,
|
|
status: 1,
|
|
time: R.isoDateTime(dayjs.utc()),
|
|
msg: "TEST NOTIFICATION MESSAGE",
|
|
ping: 278,
|
|
important: true,
|
|
duration: 8,
|
|
};
|
|
}
|
|
static generateTestMonitor(){
|
|
return {
|
|
id: 5,
|
|
name: "Test Notification Monitor",
|
|
url: "https://www.example.com",
|
|
method: "Get",
|
|
body: "OK",
|
|
headers: null,
|
|
hostname: "www.example.com",
|
|
port: 443,
|
|
maxretries: 2,
|
|
weight: 2000,
|
|
active: 1,
|
|
type: "HTTP",
|
|
interval: 60,
|
|
retryInterval: this.retryInterval,
|
|
keyword: null,
|
|
ignoreTls: false,
|
|
upsideDown: false,
|
|
maxredirects: 10,
|
|
accepted_statuscodes: ["200-299"],
|
|
dns_resolve_type: "A",
|
|
dns_resolve_server: "1.1.1.1",
|
|
dns_last_result: null,
|
|
pushToken: null,
|
|
notificationIDList:{"1":true,"5":true},
|
|
tags: [{"id":21,"monitor_id":16,"tag_id":2,"value":"","name":"Internal","color":"#059669"}],
|
|
};
|
|
}
|
|
providerList = {};
|
|
|
|
static init() {
|
|
console.log("Prepare Notification Providers");
|
|
|
|
this.providerList = {};
|
|
|
|
const list = [
|
|
new Apprise(),
|
|
new AliyunSms(),
|
|
new DingDing(),
|
|
new Discord(),
|
|
new Teams(),
|
|
new Gotify(),
|
|
new Line(),
|
|
new LunaSea(),
|
|
new Feishu(),
|
|
new Mattermost(),
|
|
new Matrix(),
|
|
new Octopush(),
|
|
new PromoSMS(),
|
|
new Pushbullet(),
|
|
new Pushover(),
|
|
new Pushy(),
|
|
new RocketChat(),
|
|
new Signal(),
|
|
new Slack(),
|
|
new SMTP(),
|
|
new Telegram(),
|
|
new Webhook(),
|
|
];
|
|
|
|
for (let item of list) {
|
|
if (! item.name) {
|
|
throw new Error("Notification provider without name");
|
|
}
|
|
|
|
if (this.providerList[item.name]) {
|
|
throw new Error("Duplicate notification provider name");
|
|
}
|
|
this.providerList[item.name] = item;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* @param notification : BeanModel
|
|
* @param msg : string General Message
|
|
* @param monitorJSON : object Monitor details (For Up/Down only)
|
|
* @param heartbeatJSON : object Heartbeat details (For Up/Down only)
|
|
* @returns {Promise<string>} Successful msg
|
|
* Throw Error with fail msg
|
|
*/
|
|
static async send(notification, msg, monitorJSON, heartbeatJSON) {
|
|
if (this.providerList[notification.type]) {
|
|
try {
|
|
monitorJSON.health = ((heartbeatJSON.status == 1) !== monitorJSON.upsideDown) ? "✅ Healthy": "❌ Unhealthy"
|
|
|
|
let parseData = {
|
|
// I actually dont think that it is necessary to put the notification in the data sent to the template.
|
|
// notification: notification,
|
|
monitor: monitorJSON,
|
|
heartbeat: heartbeatJSON,
|
|
}
|
|
let template = this.getTemplateFromNotification(notification)
|
|
console.log(`Template: (${template})`)
|
|
let message = await engine.parseAndRender(template, parseData)
|
|
|
|
return this.providerList[notification.type].send(notification, message, monitorJSON, heartbeatJSON);
|
|
} catch (e) {
|
|
throw e
|
|
}
|
|
|
|
} else {
|
|
throw new Error("Notification type is not supported");
|
|
}
|
|
}
|
|
|
|
static getTemplateFromNotification(notification){
|
|
|
|
let template = notification.template
|
|
let detail = notification.detail
|
|
console.log(`Detail: (${detail}) Template: (${template})`)
|
|
switch (detail) {
|
|
case "Minimal Detail":
|
|
return MinimalDetailTemplate
|
|
case "Low Detail":
|
|
return LowDetailTemplate
|
|
case "Medium Detail":
|
|
return MediumDetailTemplate
|
|
case "Full Detail":
|
|
return FullDetailTemplate
|
|
case "Custom Template":
|
|
if (template) {
|
|
return template
|
|
}
|
|
//returns low in the case of a template being empty string or undefined.
|
|
}
|
|
return LowDetailTemplate
|
|
}
|
|
|
|
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);
|
|
bean.is_default = notification.isDefault || false;
|
|
await R.store(bean)
|
|
|
|
if (notification.applyExisting) {
|
|
await applyNotificationEveryMonitor(bean.id, userID);
|
|
}
|
|
|
|
return 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 checkApprise() {
|
|
let commandExistsSync = require("command-exists").sync;
|
|
let exists = commandExistsSync("apprise");
|
|
return exists;
|
|
}
|
|
|
|
}
|
|
|
|
async function applyNotificationEveryMonitor(notificationID, userID) {
|
|
let monitors = await R.getAll("SELECT id FROM monitor WHERE user_id = ?", [
|
|
userID
|
|
]);
|
|
|
|
for (let i = 0; i < monitors.length; i++) {
|
|
let checkNotification = await R.findOne("monitor_notification", " monitor_id = ? AND notification_id = ? ", [
|
|
monitors[i].id,
|
|
notificationID,
|
|
])
|
|
|
|
if (! checkNotification) {
|
|
let relation = R.dispense("monitor_notification");
|
|
relation.monitor_id = monitors[i].id;
|
|
relation.notification_id = notificationID;
|
|
await R.store(relation)
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
Notification,
|
|
}
|
|
|