mirror of
				https://github.com/louislam/uptime-kuma.git
				synced 2025-10-31 11:29:20 +08:00 
			
		
		
		
	Feat: Add Barebones certificate info display
This commit is contained in:
		| @@ -6,7 +6,7 @@ dayjs.extend(utc) | |||||||
| dayjs.extend(timezone) | dayjs.extend(timezone) | ||||||
| const axios = require("axios"); | const axios = require("axios"); | ||||||
| const {UP, DOWN, PENDING} = require("../util"); | const {UP, DOWN, PENDING} = require("../util"); | ||||||
| const {tcping, ping} = require("../util-server"); | const {tcping, ping, checkCertificate} = require("../util-server"); | ||||||
| const {R} = require("redbean-node"); | const {R} = require("redbean-node"); | ||||||
| const {BeanModel} = require("redbean-node/dist/bean-model"); | const {BeanModel} = require("redbean-node/dist/bean-model"); | ||||||
| const {Notification} = require("../notification") | const {Notification} = require("../notification") | ||||||
| @@ -79,6 +79,9 @@ class Monitor extends BeanModel { | |||||||
|                     }) |                     }) | ||||||
|                     bean.msg = `${res.status} - ${res.statusText}` |                     bean.msg = `${res.status} - ${res.statusText}` | ||||||
|                     bean.ping = dayjs().valueOf() - startTime; |                     bean.ping = dayjs().valueOf() - startTime; | ||||||
|  |                     if (this.url.startsWith("https")) { | ||||||
|  |                         Monitor.sendCertInfo(checkCertificate(res), io, this.id, this.user_id); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|                     if (this.type === "http") { |                     if (this.type === "http") { | ||||||
|                         bean.status = UP; |                         bean.status = UP; | ||||||
| @@ -218,6 +221,14 @@ class Monitor extends BeanModel { | |||||||
|         io.to(userID).emit("avgPing", monitorID, avgPing); |         io.to(userID).emit("avgPing", monitorID, avgPing); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * | ||||||
|  |      * @param checkCertificateResult : Object return result of checkCertificate | ||||||
|  |      */ | ||||||
|  |      static async sendCertInfo(checkCertificateResult, io, monitorID, userID) { | ||||||
|  |         io.to(userID).emit("certInfo", monitorID, checkCertificateResult); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Uptime with calculation |      * Uptime with calculation | ||||||
|      * Calculation based on: |      * Calculation based on: | ||||||
|   | |||||||
| @@ -70,3 +70,53 @@ exports.getSettings = async function (type) { | |||||||
|  |  | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | // ssl-checker by @dyaa | ||||||
|  | // param: res - response object from axios | ||||||
|  | // return an object containing the certificate information | ||||||
|  |  | ||||||
|  | const getDaysBetween = (validFrom, validTo) => | ||||||
|  |     Math.round(Math.abs(+validFrom - +validTo) / 8.64e7); | ||||||
|  |  | ||||||
|  | const getDaysRemaining = (validFrom, validTo) => { | ||||||
|  |     const daysRemaining = getDaysBetween(validFrom, validTo); | ||||||
|  |     if (new Date(validTo).getTime() < new Date().getTime()) { | ||||||
|  |         return -daysRemaining; | ||||||
|  |     } | ||||||
|  |     return daysRemaining; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | exports.checkCertificate = function (res) { | ||||||
|  |     const { | ||||||
|  |         valid_from, | ||||||
|  |         valid_to, | ||||||
|  |         subjectaltname, | ||||||
|  |         issuer, | ||||||
|  |         fingerprint, | ||||||
|  |     } = res.request.res.socket.getPeerCertificate(false); | ||||||
|  |  | ||||||
|  |     if (!valid_from || !valid_to || !subjectaltname) { | ||||||
|  |         reject(new Error('No certificate')); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const valid = res.request.res.socket.authorized || false; | ||||||
|  |  | ||||||
|  |     const validTo = new Date(valid_to); | ||||||
|  |  | ||||||
|  |     const validFor = subjectaltname | ||||||
|  |         .replace(/DNS:|IP Address:/g, "") | ||||||
|  |         .split(", "); | ||||||
|  |  | ||||||
|  |     const daysRemaining = getDaysRemaining(new Date(), validTo); | ||||||
|  |  | ||||||
|  |     return { | ||||||
|  |         valid, | ||||||
|  |         validFor, | ||||||
|  |         validTo, | ||||||
|  |         daysRemaining, | ||||||
|  |         issuer, | ||||||
|  |         fingerprint, | ||||||
|  |     }; | ||||||
|  | } | ||||||
| @@ -25,6 +25,7 @@ export default { | |||||||
|             importantHeartbeatList: { }, |             importantHeartbeatList: { }, | ||||||
|             avgPingList: { }, |             avgPingList: { }, | ||||||
|             uptimeList: { }, |             uptimeList: { }, | ||||||
|  |             certInfoList: {}, | ||||||
|             notificationList: [], |             notificationList: [], | ||||||
|             windowWidth: window.innerWidth, |             windowWidth: window.innerWidth, | ||||||
|             showListMobile: false, |             showListMobile: false, | ||||||
| @@ -114,6 +115,10 @@ export default { | |||||||
|             this.uptimeList[`${monitorID}_${type}`] = data |             this.uptimeList[`${monitorID}_${type}`] = data | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  |         socket.on('certInfo', (monitorID, data) => { | ||||||
|  |             this.certInfoList[monitorID] = data | ||||||
|  |         }); | ||||||
|  |  | ||||||
|         socket.on('importantHeartbeatList', (monitorID, data) => { |         socket.on('importantHeartbeatList', (monitorID, data) => { | ||||||
|             if (! (monitorID in this.importantHeartbeatList)) { |             if (! (monitorID in this.importantHeartbeatList)) { | ||||||
|                 this.importantHeartbeatList[monitorID] = data; |                 this.importantHeartbeatList[monitorID] = data; | ||||||
|   | |||||||
| @@ -54,6 +54,38 @@ | |||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|  |     <div class="shadow-box big-padding text-center stats" v-if="monitor.type === 'http' && monitor.url.startsWith('https') && certInfo != null"> | ||||||
|  |         <div class="row"> | ||||||
|  |             <div class="col"> | ||||||
|  |                 <h4>Certificate Info</h4> | ||||||
|  |                 <table class="text-start"> | ||||||
|  |                     <tbody> | ||||||
|  |                         <tr class="my-3"> | ||||||
|  |                             <td class="px-3">Valid: </td> | ||||||
|  |                             <td>{{ certInfo.valid }}</td> | ||||||
|  |                         </tr> | ||||||
|  |                         <tr class="my-3"> | ||||||
|  |                             <td class="px-3">Valid To: </td> | ||||||
|  |                             <td>{{ certInfo.validTo ? new Date(certInfo.validTo).toLocaleString() : "" }}</td> | ||||||
|  |                         </tr> | ||||||
|  |                         <tr class="my-3"> | ||||||
|  |                             <td class="px-3">Days Remaining: </td> | ||||||
|  |                             <td>{{ certInfo.daysRemaining }}</td> | ||||||
|  |                         </tr> | ||||||
|  |                         <tr class="my-3"> | ||||||
|  |                             <td class="px-3">Issuer: </td> | ||||||
|  |                             <td>{{ certInfo.issuer }}</td> | ||||||
|  |                         </tr> | ||||||
|  |                         <tr class="my-3"> | ||||||
|  |                             <td class="px-3">Fingerprint: </td> | ||||||
|  |                             <td>{{ certInfo.fingerprint }}</td> | ||||||
|  |                         </tr> | ||||||
|  |                     </tbody> | ||||||
|  |                 </table> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|     <div class="shadow-box"> |     <div class="shadow-box"> | ||||||
|         <table class="table table-borderless table-hover"> |         <table class="table table-borderless table-hover"> | ||||||
|             <thead> |             <thead> | ||||||
| @@ -180,6 +212,14 @@ export default { | |||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  |  | ||||||
|  |         certInfo() { | ||||||
|  |             if (this.$root.certInfoList[this.monitor.id]) { | ||||||
|  |                 return this.$root.certInfoList[this.monitor.id] | ||||||
|  |             } else { | ||||||
|  |                 return { } | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |  | ||||||
|         displayedRecords() { |         displayedRecords() { | ||||||
|             const startIndex = this.perPage * (this.page - 1); |             const startIndex = this.perPage * (this.page - 1); | ||||||
|             const endIndex = startIndex + this.perPage; |             const endIndex = startIndex + this.perPage; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user