mirror of
				https://github.com/louislam/uptime-kuma.git
				synced 2025-11-04 21:56:12 +08:00 
			
		
		
		
	Merge pull request #781 from louislam/cert-notification
Certificate renewal notifications
This commit is contained in:
		
							
								
								
									
										18
									
								
								db/patch-notification_sent_history.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								db/patch-notification_sent_history.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
 | 
				
			||||||
 | 
					BEGIN TRANSACTION;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CREATE TABLE [notification_sent_history] (
 | 
				
			||||||
 | 
					    [id] INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
 | 
				
			||||||
 | 
					    [type] VARCHAR(50) NOT NULL,
 | 
				
			||||||
 | 
					    [monitor_id] INTEGER NOT NULL,
 | 
				
			||||||
 | 
					    [days] INTEGER NOT NULL,
 | 
				
			||||||
 | 
					    UNIQUE([type], [monitor_id], [days])
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CREATE INDEX [good_index] ON [notification_sent_history] (
 | 
				
			||||||
 | 
					    [type],
 | 
				
			||||||
 | 
					    [monitor_id],
 | 
				
			||||||
 | 
					    [days]
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COMMIT;
 | 
				
			||||||
@@ -51,6 +51,7 @@ class Database {
 | 
				
			|||||||
        "patch-monitor-push_token.sql": true,
 | 
					        "patch-monitor-push_token.sql": true,
 | 
				
			||||||
        "patch-http-monitor-method-body-and-headers.sql": true,
 | 
					        "patch-http-monitor-method-body-and-headers.sql": true,
 | 
				
			||||||
        "patch-2fa-invalidate-used-token.sql": true,
 | 
					        "patch-2fa-invalidate-used-token.sql": true,
 | 
				
			||||||
 | 
					        "patch-notification_sent_history.sql": true,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,7 @@ const jobs = [
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        name: "clear-old-data",
 | 
					        name: "clear-old-data",
 | 
				
			||||||
        interval: "at 03:14",
 | 
					        interval: "at 03:14",
 | 
				
			||||||
    }
 | 
					    },
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const initBackgroundJobs = function (args) {
 | 
					const initBackgroundJobs = function (args) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -168,7 +168,14 @@ class Monitor extends BeanModel {
 | 
				
			|||||||
                    let certInfoStartTime = dayjs().valueOf();
 | 
					                    let certInfoStartTime = dayjs().valueOf();
 | 
				
			||||||
                    if (this.getUrl()?.protocol === "https:") {
 | 
					                    if (this.getUrl()?.protocol === "https:") {
 | 
				
			||||||
                        try {
 | 
					                        try {
 | 
				
			||||||
                            tlsInfo = await this.updateTlsInfo(checkCertificate(res));
 | 
					                            let tlsInfoObject = checkCertificate(res);
 | 
				
			||||||
 | 
					                            tlsInfo = await this.updateTlsInfo(tlsInfoObject);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            if (!this.getIgnoreTls()) {
 | 
				
			||||||
 | 
					                                debug("call sendCertNotification");
 | 
				
			||||||
 | 
					                                await this.sendCertNotification(tlsInfoObject);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        } catch (e) {
 | 
					                        } catch (e) {
 | 
				
			||||||
                            if (e.message !== "No TLS certificate in response") {
 | 
					                            if (e.message !== "No TLS certificate in response") {
 | 
				
			||||||
                                console.error(e.message);
 | 
					                                console.error(e.message);
 | 
				
			||||||
@@ -444,10 +451,36 @@ class Monitor extends BeanModel {
 | 
				
			|||||||
        let tls_info_bean = await R.findOne("monitor_tls_info", "monitor_id = ?", [
 | 
					        let tls_info_bean = await R.findOne("monitor_tls_info", "monitor_id = ?", [
 | 
				
			||||||
            this.id,
 | 
					            this.id,
 | 
				
			||||||
        ]);
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (tls_info_bean == null) {
 | 
					        if (tls_info_bean == null) {
 | 
				
			||||||
            tls_info_bean = R.dispense("monitor_tls_info");
 | 
					            tls_info_bean = R.dispense("monitor_tls_info");
 | 
				
			||||||
            tls_info_bean.monitor_id = this.id;
 | 
					            tls_info_bean.monitor_id = this.id;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Clear sent history if the cert changed.
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                let oldCertInfo = JSON.parse(tls_info_bean.info_json);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let isValidObjects = oldCertInfo && oldCertInfo.certInfo && checkCertificateResult && checkCertificateResult.certInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (isValidObjects) {
 | 
				
			||||||
 | 
					                    if (oldCertInfo.certInfo.fingerprint256 !== checkCertificateResult.certInfo.fingerprint256) {
 | 
				
			||||||
 | 
					                        debug("Resetting sent_history");
 | 
				
			||||||
 | 
					                        await R.exec("DELETE FROM notification_sent_history WHERE type = 'certificate' AND monitor_id = ?", [
 | 
				
			||||||
 | 
					                            this.id
 | 
				
			||||||
 | 
					                        ]);
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        debug("No need to reset sent_history");
 | 
				
			||||||
 | 
					                        debug(oldCertInfo.certInfo.fingerprint256);
 | 
				
			||||||
 | 
					                        debug(checkCertificateResult.certInfo.fingerprint256);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    debug("Not valid object");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } catch (e) { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        tls_info_bean.info_json = JSON.stringify(checkCertificateResult);
 | 
					        tls_info_bean.info_json = JSON.stringify(checkCertificateResult);
 | 
				
			||||||
        await R.store(tls_info_bean);
 | 
					        await R.store(tls_info_bean);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -595,9 +628,7 @@ class Monitor extends BeanModel {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    static async sendNotification(isFirstBeat, monitor, bean) {
 | 
					    static async sendNotification(isFirstBeat, monitor, bean) {
 | 
				
			||||||
        if (!isFirstBeat || bean.status === DOWN) {
 | 
					        if (!isFirstBeat || bean.status === DOWN) {
 | 
				
			||||||
            let notificationList = await R.getAll("SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id ", [
 | 
					            const notificationList = await Monitor.getNotificationList(monitor);
 | 
				
			||||||
                monitor.id,
 | 
					 | 
				
			||||||
            ]);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let text;
 | 
					            let text;
 | 
				
			||||||
            if (bean.status === UP) {
 | 
					            if (bean.status === UP) {
 | 
				
			||||||
@@ -619,6 +650,70 @@ class Monitor extends BeanModel {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static async getNotificationList(monitor) {
 | 
				
			||||||
 | 
					        let notificationList = await R.getAll("SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id ", [
 | 
				
			||||||
 | 
					            monitor.id,
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        return notificationList;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async sendCertNotification(tlsInfoObject) {
 | 
				
			||||||
 | 
					        if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) {
 | 
				
			||||||
 | 
					            const notificationList = await Monitor.getNotificationList(this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            debug("call sendCertNotificationByTargetDays");
 | 
				
			||||||
 | 
					            await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, 21, notificationList);
 | 
				
			||||||
 | 
					            await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, 14, notificationList);
 | 
				
			||||||
 | 
					            await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, 7, notificationList);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async sendCertNotificationByTargetDays(daysRemaining, targetDays, notificationList) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (daysRemaining > targetDays) {
 | 
				
			||||||
 | 
					            debug(`No need to send cert notification. ${daysRemaining} > ${targetDays}`);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (notificationList.length > 0) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            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) {
 | 
				
			||||||
 | 
					                debug("Sent already, no need to send again");
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let sent = false;
 | 
				
			||||||
 | 
					            debug("Send certificate notification");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (let notification of notificationList) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    debug("Sending to " + notification.name);
 | 
				
			||||||
 | 
					                    await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] Certificate will be expired in ${daysRemaining} days`);
 | 
				
			||||||
 | 
					                    sent = true;
 | 
				
			||||||
 | 
					                } catch (e) {
 | 
				
			||||||
 | 
					                    console.error("Cannot send cert notification to " + notification.name);
 | 
				
			||||||
 | 
					                    console.error(e);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (sent) {
 | 
				
			||||||
 | 
					                await R.exec("INSERT INTO notification_sent_history (type, monitor_id, days) VALUES(?, ?, ?)", [
 | 
				
			||||||
 | 
					                    "certificate",
 | 
				
			||||||
 | 
					                    this.id,
 | 
				
			||||||
 | 
					                    targetDays,
 | 
				
			||||||
 | 
					                ]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            debug("No notification, no need to send cert notification");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = Monitor;
 | 
					module.exports = Monitor;
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user