Merge branch 'master' into feature/add-channel-notification-for-slack

This commit is contained in:
duane
2023-05-31 10:31:33 -05:00
committed by GitHub
29 changed files with 1174 additions and 550 deletions

View File

@@ -365,8 +365,8 @@ class Monitor extends BeanModel {
tlsInfo = await this.updateTlsInfo(tlsInfoObject);
if (!this.getIgnoreTls() && this.isEnabledExpiryNotification()) {
log.debug("monitor", `[${this.name}] call sendCertNotification`);
await this.sendCertNotification(tlsInfoObject);
log.debug("monitor", `[${this.name}] call checkCertExpiryNotifications`);
await this.checkCertExpiryNotifications(tlsInfoObject);
}
} catch (e) {
@@ -1212,13 +1212,19 @@ class Monitor extends BeanModel {
}
/**
* Send notification about a certificate
* checks certificate chain for expiring certificates
* @param {Object} tlsInfoObject Information about certificate
*/
async sendCertNotification(tlsInfoObject) {
async checkCertExpiryNotifications(tlsInfoObject) {
if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) {
const notificationList = await Monitor.getNotificationList(this);
if (! notificationList.length > 0) {
// fail fast. If no notification is set, all the following checks can be skipped.
log.debug("monitor", "No notification, no need to send cert notification");
return;
}
let notifyDays = await setting("tlsExpiryNotifyDays");
if (notifyDays == null || !Array.isArray(notifyDays)) {
// Reset Default
@@ -1226,10 +1232,19 @@ class Monitor extends BeanModel {
notifyDays = [ 7, 14, 21 ];
}
if (notifyDays != null && Array.isArray(notifyDays)) {
for (const day of notifyDays) {
log.debug("monitor", "call sendCertNotificationByTargetDays", day);
await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, day, notificationList);
if (Array.isArray(notifyDays)) {
for (const targetDays of notifyDays) {
let certInfo = tlsInfoObject.certInfo;
while (certInfo) {
let subjectCN = certInfo.subject["CN"];
if (certInfo.daysRemaining > targetDays) {
log.debug("monitor", `No need to send cert notification for ${certInfo.certType} certificate "${subjectCN}" (${certInfo.daysRemaining} days valid) on ${targetDays} deadline.`);
} else {
log.debug("monitor", `call sendCertNotificationByTargetDays for ${targetDays} deadline on certificate ${subjectCN}.`);
await this.sendCertNotificationByTargetDays(subjectCN, certInfo.certType, certInfo.daysRemaining, targetDays, notificationList);
}
certInfo = certInfo.issuerCertificate;
}
}
}
}
@@ -1238,55 +1253,47 @@ class Monitor extends BeanModel {
/**
* Send a certificate notification when certificate expires in less
* than target days
* @param {number} daysRemaining Number of days remaining on certifcate
* @param {string} certCN Common Name attribute from the certificate subject
* @param {string} certType certificate type
* @param {number} daysRemaining Number of days remaining on certificate
* @param {number} targetDays Number of days to alert after
* @param {LooseObject<any>[]} notificationList List of notification providers
* @returns {Promise<void>}
*/
async sendCertNotificationByTargetDays(daysRemaining, targetDays, notificationList) {
async sendCertNotificationByTargetDays(certCN, certType, daysRemaining, targetDays, notificationList) {
if (daysRemaining > targetDays) {
log.debug("monitor", `No need to send cert notification. ${daysRemaining} > ${targetDays}`);
let row = await R.getRow("SELECT * FROM notification_sent_history WHERE type = ? AND monitor_id = ? AND days <= ?", [
"certificate",
this.id,
targetDays,
]);
// Sent already, no need to send again
if (row) {
log.debug("monitor", "Sent already, no need to send again");
return;
}
if (notificationList.length > 0) {
let sent = false;
log.debug("monitor", "Send certificate notification");
let row = await R.getRow("SELECT * FROM notification_sent_history WHERE type = ? AND monitor_id = ? AND days <= ?", [
for (let notification of notificationList) {
try {
log.debug("monitor", "Sending to " + notification.name);
await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] ${certType} certificate ${certCN} will be expired in ${daysRemaining} days`);
sent = true;
} catch (e) {
log.error("monitor", "Cannot send cert notification to " + notification.name);
log.error("monitor", e);
}
}
if (sent) {
await R.exec("INSERT INTO notification_sent_history (type, monitor_id, days) VALUES(?, ?, ?)", [
"certificate",
this.id,
targetDays,
]);
// Sent already, no need to send again
if (row) {
log.debug("monitor", "Sent already, no need to send again");
return;
}
let sent = false;
log.debug("monitor", "Send certificate notification");
for (let notification of notificationList) {
try {
log.debug("monitor", "Sending to " + notification.name);
await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] Certificate will expire in ${daysRemaining} days`);
sent = true;
} catch (e) {
log.error("monitor", "Cannot send cert notification to " + notification.name);
log.error("monitor", e);
}
}
if (sent) {
await R.exec("INSERT INTO notification_sent_history (type, monitor_id, days) VALUES(?, ?, ?)", [
"certificate",
this.id,
targetDays,
]);
}
} else {
log.debug("monitor", "No notification, no need to send cert notification");
}
}

View File

@@ -59,8 +59,8 @@ class Discord extends NotificationProvider {
value: monitorJSON["type"] === "push" ? "Heartbeat" : address,
},
{
name: "Time (UTC)",
value: heartbeatJSON["time"],
name: `Time (${heartbeatJSON["timezone"]})`,
value: heartbeatJSON["localDateTime"],
},
{
name: "Error",
@@ -94,8 +94,8 @@ class Discord extends NotificationProvider {
value: monitorJSON["type"] === "push" ? "Heartbeat" : address,
},
{
name: "Time (UTC)",
value: heartbeatJSON["time"],
name: `Time (${heartbeatJSON["timezone"]})`,
value: heartbeatJSON["localDateTime"],
},
{
name: "Ping",

View File

@@ -35,8 +35,7 @@ class Feishu extends NotificationProvider {
text:
"[Down] " +
heartbeatJSON["msg"] +
"\nTime (UTC): " +
heartbeatJSON["time"],
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
},
],
],
@@ -62,8 +61,7 @@ class Feishu extends NotificationProvider {
text:
"[Up] " +
heartbeatJSON["msg"] +
"\nTime (UTC): " +
heartbeatJSON["time"],
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`,
},
],
],

View File

@@ -33,7 +33,10 @@ class Line extends NotificationProvider {
"messages": [
{
"type": "text",
"text": "UptimeKuma Alert: [🔴 Down]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
"text": "UptimeKuma Alert: [🔴 Down]\n" +
"Name: " + monitorJSON["name"] + " \n" +
heartbeatJSON["msg"] +
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
}
]
};
@@ -44,7 +47,10 @@ class Line extends NotificationProvider {
"messages": [
{
"type": "text",
"text": "UptimeKuma Alert: [✅ Up]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
"text": "UptimeKuma Alert: [✅ Up]\n" +
"Name: " + monitorJSON["name"] + " \n" +
heartbeatJSON["msg"] +
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
}
]
};

View File

@@ -24,12 +24,18 @@ class LineNotify extends NotificationProvider {
await axios.post(lineAPIUrl, qs.stringify(testMessage), config);
} else if (heartbeatJSON["status"] === DOWN) {
let downMessage = {
"message": "\n[🔴 Down]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
"message": "\n[🔴 Down]\n" +
"Name: " + monitorJSON["name"] + " \n" +
heartbeatJSON["msg"] + "\n" +
`Time (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
};
await axios.post(lineAPIUrl, qs.stringify(downMessage), config);
} else if (heartbeatJSON["status"] === UP) {
let upMessage = {
"message": "\n[✅ Up]\n" + "Name: " + monitorJSON["name"] + " \n" + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"]
"message": "\n[✅ Up]\n" +
"Name: " + monitorJSON["name"] + " \n" +
heartbeatJSON["msg"] + "\n" +
`Time (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
};
await axios.post(lineAPIUrl, qs.stringify(upMessage), config);
}

View File

@@ -28,7 +28,9 @@ class LunaSea extends NotificationProvider {
if (heartbeatJSON["status"] === DOWN) {
let downdata = {
"title": "UptimeKuma Alert: " + monitorJSON["name"],
"body": "[🔴 Down] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
"body": "[🔴 Down] " +
heartbeatJSON["msg"] +
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
};
await axios.post(lunaseaurl, downdata);
return okMsg;
@@ -37,7 +39,9 @@ class LunaSea extends NotificationProvider {
if (heartbeatJSON["status"] === UP) {
let updata = {
"title": "UptimeKuma Alert: " + monitorJSON["name"],
"body": "[✅ Up] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
"body": "[✅ Up] " +
heartbeatJSON["msg"] +
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`
};
await axios.post(lunaseaurl, updata);
return okMsg;

View File

@@ -88,8 +88,8 @@ class Mattermost extends NotificationProvider {
statusField,
{
short: true,
title: "Time (UTC)",
value: heartbeatJSON.time,
title: `Time (${heartbeatJSON["timezone"]})`,
value: heartbeatJSON.localDateTime,
},
],
},

View File

@@ -29,14 +29,18 @@ class Pushbullet extends NotificationProvider {
let downData = {
"type": "note",
"title": "UptimeKuma Alert: " + monitorJSON["name"],
"body": "[🔴 Down] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
"body": "[🔴 Down] " +
heartbeatJSON["msg"] +
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`,
};
await axios.post(pushbulletUrl, downData, config);
} else if (heartbeatJSON["status"] === UP) {
let upData = {
"type": "note",
"title": "UptimeKuma Alert: " + monitorJSON["name"],
"body": "[✅ Up] " + heartbeatJSON["msg"] + "\nTime (UTC): " + heartbeatJSON["time"],
"body": "[✅ Up] " +
heartbeatJSON["msg"] +
`\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`,
};
await axios.post(pushbulletUrl, upData, config);
}

View File

@@ -24,13 +24,16 @@ class Pushover extends NotificationProvider {
if (notification.pushoverdevice) {
data.device = notification.pushoverdevice;
}
if (notification.pushoverttl) {
data.ttl = notification.pushoverttl;
}
try {
if (heartbeatJSON == null) {
await axios.post(pushoverlink, data);
return okMsg;
} else {
data.message += "\n<b>Time (UTC)</b>:" + heartbeatJSON["time"];
data.message += `\n<b>Time (${heartbeatJSON["timezone"]})</b>:${heartbeatJSON["localDateTime"]}`;
await axios.post(pushoverlink, data);
return okMsg;
}

View File

@@ -22,8 +22,6 @@ class RocketChat extends NotificationProvider {
return okMsg;
}
const time = heartbeatJSON["time"];
let data = {
"text": "Uptime Kuma Alert",
"channel": notification.rocketchannel,
@@ -31,7 +29,7 @@ class RocketChat extends NotificationProvider {
"icon_emoji": notification.rocketiconemo,
"attachments": [
{
"title": "Uptime Kuma Alert *Time (UTC)*\n" + time,
"title": `Uptime Kuma Alert *Time (${heartbeatJSON["timezone"]})*\n${heartbeatJSON["localDateTime"]}`,
"text": "*Message*\n" + msg,
}
]

View File

@@ -44,7 +44,6 @@ class Slack extends NotificationProvider {
return okMsg;
}
const time = heartbeatJSON["time"];
const textMsg = "Uptime Kuma Alert";
let data = {
"text": `${textMsg}\n${msg}`,
@@ -70,7 +69,7 @@ class Slack extends NotificationProvider {
},
{
"type": "mrkdwn",
"text": "*Time (UTC)*\n" + time,
"text": `*Time (${heartbeatJSON["timezone"]})*\n${heartbeatJSON["localDateTime"]}`,
}],
}
],

View File

@@ -91,7 +91,7 @@ class SMTP extends NotificationProvider {
let bodyTextContent = msg;
if (heartbeatJSON) {
bodyTextContent = `${msg}\nTime (UTC): ${heartbeatJSON["time"]}`;
bodyTextContent = `${msg}\nTime (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`;
}
// send mail with defined transport object

View File

@@ -19,6 +19,11 @@ const nodeVersion = parseInt(process.versions.node.split(".")[0]);
const requiredVersion = 14;
console.log(`Your Node.js version: ${nodeVersion}`);
// See more: https://github.com/louislam/uptime-kuma/issues/3138
if (nodeVersion >= 20) {
console.warn("\x1b[31m%s\x1b[0m", "Warning: Uptime Kuma is currently not stable on Node.js >= 20, please use Node.js 18.");
}
if (nodeVersion < requiredVersion) {
console.error(`Error: Your Node.js version is not supported, please upgrade to Node.js >= ${requiredVersion}.`);
process.exit(-1);

View File

@@ -519,12 +519,16 @@ const parseCertificateInfo = function (info) {
// Move up the chain until loop is encountered
if (link.issuerCertificate == null) {
link.certType = (i === 0) ? "self-signed" : "root CA";
break;
} else if (link.issuerCertificate.fingerprint in existingList) {
// a root CA certificate is typically "signed by itself" (=> "self signed certificate") and thus the "issuerCertificate" is a reference to itself.
log.debug("cert", `[Last] ${link.issuerCertificate.fingerprint}`);
link.certType = (i === 0) ? "self-signed" : "root CA";
link.issuerCertificate = null;
break;
} else {
link.certType = (i === 0) ? "server" : "intermediate CA";
link = link.issuerCertificate;
}