mirror of
				https://github.com/louislam/uptime-kuma.git
				synced 2025-10-31 03:19:20 +08:00 
			
		
		
		
	Feat: Add Barebones certificate info display
This commit is contained in:
		| @@ -6,7 +6,7 @@ dayjs.extend(utc) | ||||
| dayjs.extend(timezone) | ||||
| const axios = require("axios"); | ||||
| 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 {BeanModel} = require("redbean-node/dist/bean-model"); | ||||
| const {Notification} = require("../notification") | ||||
| @@ -79,6 +79,9 @@ class Monitor extends BeanModel { | ||||
|                     }) | ||||
|                     bean.msg = `${res.status} - ${res.statusText}` | ||||
|                     bean.ping = dayjs().valueOf() - startTime; | ||||
|                     if (this.url.startsWith("https")) { | ||||
|                         Monitor.sendCertInfo(checkCertificate(res), io, this.id, this.user_id); | ||||
|                     } | ||||
|  | ||||
|                     if (this.type === "http") { | ||||
|                         bean.status = UP; | ||||
| @@ -218,6 +221,14 @@ class Monitor extends BeanModel { | ||||
|         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 | ||||
|      * Calculation based on: | ||||
|   | ||||
| @@ -70,3 +70,53 @@ exports.getSettings = async function (type) { | ||||
|  | ||||
|     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: { }, | ||||
|             avgPingList: { }, | ||||
|             uptimeList: { }, | ||||
|             certInfoList: {}, | ||||
|             notificationList: [], | ||||
|             windowWidth: window.innerWidth, | ||||
|             showListMobile: false, | ||||
| @@ -114,6 +115,10 @@ export default { | ||||
|             this.uptimeList[`${monitorID}_${type}`] = data | ||||
|         }); | ||||
|  | ||||
|         socket.on('certInfo', (monitorID, data) => { | ||||
|             this.certInfoList[monitorID] = data | ||||
|         }); | ||||
|  | ||||
|         socket.on('importantHeartbeatList', (monitorID, data) => { | ||||
|             if (! (monitorID in this.importantHeartbeatList)) { | ||||
|                 this.importantHeartbeatList[monitorID] = data; | ||||
|   | ||||
| @@ -54,6 +54,38 @@ | ||||
|         </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"> | ||||
|         <table class="table table-borderless table-hover"> | ||||
|             <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() { | ||||
|             const startIndex = this.perPage * (this.page - 1); | ||||
|             const endIndex = startIndex + this.perPage; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user