mirror of
				https://github.com/louislam/uptime-kuma.git
				synced 2025-10-31 03:19:20 +08:00 
			
		
		
		
	Merge pull request #3357 from tarun7singh/status-page-expiry
Status page certificate expiry
This commit is contained in:
		
							
								
								
									
										7
									
								
								db/patch-add-certificate-expiry-status-page.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								db/patch-add-certificate-expiry-status-page.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| -- You should not modify if this have pushed to Github, unless it does serious wrong with the db. | ||||
| BEGIN TRANSACTION; | ||||
|  | ||||
| ALTER TABLE status_page | ||||
|     ADD show_certificate_expiry BOOLEAN default 0 NOT NULL; | ||||
|  | ||||
| COMMIT; | ||||
| @@ -74,6 +74,7 @@ class Database { | ||||
|         "patch-add-invert-keyword.sql": true, | ||||
|         "patch-added-json-query.sql": true, | ||||
|         "patch-added-kafka-producer.sql": true, | ||||
|         "patch-add-certificate-expiry-status-page.sql": true, | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -9,12 +9,12 @@ class Group extends BeanModel { | ||||
|      * @param {boolean} [showTags=false] Should the JSON include monitor tags | ||||
|      * @returns {Object} | ||||
|      */ | ||||
|     async toPublicJSON(showTags = false) { | ||||
|     async toPublicJSON(showTags = false, certExpiry = false) { | ||||
|         let monitorBeanList = await this.getMonitorList(); | ||||
|         let monitorList = []; | ||||
|  | ||||
|         for (let bean of monitorBeanList) { | ||||
|             monitorList.push(await bean.toPublicJSON(showTags)); | ||||
|             monitorList.push(await bean.toPublicJSON(showTags, certExpiry)); | ||||
|         } | ||||
|  | ||||
|         return { | ||||
|   | ||||
| @@ -37,11 +37,12 @@ class Monitor extends BeanModel { | ||||
|      * Only show necessary data to public | ||||
|      * @returns {Object} | ||||
|      */ | ||||
|     async toPublicJSON(showTags = false) { | ||||
|     async toPublicJSON(showTags = false, certExpiry = false) { | ||||
|         let obj = { | ||||
|             id: this.id, | ||||
|             name: this.name, | ||||
|             sendUrl: this.sendUrl, | ||||
|             type: this.type, | ||||
|         }; | ||||
|  | ||||
|         if (this.sendUrl) { | ||||
| @@ -51,6 +52,13 @@ class Monitor extends BeanModel { | ||||
|         if (showTags) { | ||||
|             obj.tags = await this.getTags(); | ||||
|         } | ||||
|  | ||||
|         if (certExpiry && this.type === "http") { | ||||
|             const { certExpiryDaysRemaining, validCert } = await this.getCertExpiry(this.id); | ||||
|             obj.certExpiryDaysRemaining = certExpiryDaysRemaining; | ||||
|             obj.validCert = validCert; | ||||
|         } | ||||
|  | ||||
|         return obj; | ||||
|     } | ||||
|  | ||||
| @@ -184,6 +192,31 @@ class Monitor extends BeanModel { | ||||
|         return await R.getAll("SELECT mt.*, tag.name, tag.color FROM monitor_tag mt JOIN tag ON mt.tag_id = tag.id WHERE mt.monitor_id = ? ORDER BY tag.name", [ this.id ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets certificate expiry for this monitor | ||||
|      * @param {number} monitorID ID of monitor to send | ||||
|      * @returns {Promise<LooseObject<any>>} | ||||
|      */ | ||||
|     async getCertExpiry(monitorID) { | ||||
|         let tlsInfoBean = await R.findOne("monitor_tls_info", "monitor_id = ?", [ | ||||
|             monitorID, | ||||
|         ]); | ||||
|         let tlsInfo; | ||||
|         if (tlsInfoBean) { | ||||
|             tlsInfo = JSON.parse(tlsInfoBean?.info_json); | ||||
|             if (tlsInfo?.valid && tlsInfo?.certInfo?.daysRemaining) { | ||||
|                 return { | ||||
|                     certExpiryDaysRemaining: tlsInfo.certInfo.daysRemaining, | ||||
|                     validCert: true | ||||
|                 }; | ||||
|             } | ||||
|         } | ||||
|         return { | ||||
|             certExpiryDaysRemaining: "", | ||||
|             validCert: false | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Encode user and password to Base64 encoding | ||||
|      * for HTTP "basic" auth, as per RFC-7617 | ||||
|   | ||||
| @@ -90,6 +90,8 @@ class StatusPage extends BeanModel { | ||||
|      * @param {StatusPage} statusPage | ||||
|      */ | ||||
|     static async getStatusPageData(statusPage) { | ||||
|         const config = await statusPage.toPublicJSON(); | ||||
|  | ||||
|         // Incident | ||||
|         let incident = await R.findOne("incident", " pin = 1 AND active = 1 AND status_page_id = ? ", [ | ||||
|             statusPage.id, | ||||
| @@ -110,13 +112,13 @@ class StatusPage extends BeanModel { | ||||
|         ]); | ||||
|  | ||||
|         for (let groupBean of list) { | ||||
|             let monitorGroup = await groupBean.toPublicJSON(showTags); | ||||
|             let monitorGroup = await groupBean.toPublicJSON(showTags, config?.showCertificateExpiry); | ||||
|             publicGroupList.push(monitorGroup); | ||||
|         } | ||||
|  | ||||
|         // Response | ||||
|         return { | ||||
|             config: await statusPage.toPublicJSON(), | ||||
|             config, | ||||
|             incident, | ||||
|             publicGroupList, | ||||
|             maintenanceList, | ||||
| @@ -234,6 +236,7 @@ class StatusPage extends BeanModel { | ||||
|             footerText: this.footer_text, | ||||
|             showPoweredBy: !!this.show_powered_by, | ||||
|             googleAnalyticsId: this.google_analytics_tag_id, | ||||
|             showCertificateExpiry: !!this.show_certificate_expiry, | ||||
|         }; | ||||
|     } | ||||
|  | ||||
| @@ -255,6 +258,7 @@ class StatusPage extends BeanModel { | ||||
|             footerText: this.footer_text, | ||||
|             showPoweredBy: !!this.show_powered_by, | ||||
|             googleAnalyticsId: this.google_analytics_tag_id, | ||||
|             showCertificateExpiry: !!this.show_certificate_expiry, | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -162,6 +162,7 @@ module.exports.statusPageSocketHandler = (socket) => { | ||||
|             statusPage.footer_text = config.footerText; | ||||
|             statusPage.custom_css = config.customCSS; | ||||
|             statusPage.show_powered_by = config.showPoweredBy; | ||||
|             statusPage.show_certificate_expiry = config.showCertificateExpiry; | ||||
|             statusPage.modified_date = R.isoDateTime(); | ||||
|             statusPage.google_analytics_tag_id = config.googleAnalyticsId; | ||||
|  | ||||
|   | ||||
| @@ -61,8 +61,13 @@ | ||||
|                                                 /> | ||||
|                                             </span> | ||||
|                                         </div> | ||||
|                                         <div v-if="showTags" class="tags"> | ||||
|                                             <Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" /> | ||||
|                                         <div class="extra-info"> | ||||
|                                             <div v-if="showCertificateExpiry && monitor.element.type === 'http'"> | ||||
|                                                 <Tag :item="{name: $t('Cert Exp.'), value: formattedCertExpiryMessage(monitor), color: certExpiryColor(monitor)}" :size="'sm'" /> | ||||
|                                             </div> | ||||
|                                             <div v-if="showTags"> | ||||
|                                                 <Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" /> | ||||
|                                             </div> | ||||
|                                         </div> | ||||
|                                     </div> | ||||
|                                     <div :key="$root.userHeartbeatBar" class="col-3 col-md-4"> | ||||
| @@ -103,6 +108,10 @@ export default { | ||||
|         /** Should tags be shown? */ | ||||
|         showTags: { | ||||
|             type: Boolean, | ||||
|         }, | ||||
|         /** Should expiry be shown? */ | ||||
|         showCertificateExpiry: { | ||||
|             type: Boolean, | ||||
|         } | ||||
|     }, | ||||
|     data() { | ||||
| @@ -154,6 +163,33 @@ export default { | ||||
|             } | ||||
|             return monitor.element.sendUrl && monitor.element.url && monitor.element.url !== "https://" && !this.editMode; | ||||
|         }, | ||||
|  | ||||
|         /** | ||||
|          * Returns formatted certificate expiry or Bad cert message | ||||
|          * @param {Object} monitor Monitor to show expiry for | ||||
|          * @returns {string} | ||||
|          */ | ||||
|         formattedCertExpiryMessage(monitor) { | ||||
|             if (monitor?.element?.validCert && monitor?.element?.certExpiryDaysRemaining) { | ||||
|                 return monitor.element.certExpiryDaysRemaining + " " + this.$tc("day", monitor.element.certExpiryDaysRemaining); | ||||
|             } else if (monitor?.element?.validCert === false) { | ||||
|                 return this.$t("noOrBadCertificate"); | ||||
|             } else { | ||||
|                 return this.$t("Unknown") + " " + this.$tc("day", 2); | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         /** | ||||
|          * Returns certificate expiry based on days remaining | ||||
|          * @param {Object} monitor Monitor to show expiry for | ||||
|          * @returns {string} | ||||
|          */ | ||||
|         certExpiryColor(monitor) { | ||||
|             if (monitor?.element?.validCert && monitor.element.certExpiryDaysRemaining > 7) { | ||||
|                 return "#059669"; | ||||
|             } | ||||
|             return "#DC2626"; | ||||
|         }, | ||||
|     } | ||||
| }; | ||||
| </script> | ||||
| @@ -161,6 +197,15 @@ export default { | ||||
| <style lang="scss" scoped> | ||||
| @import "../assets/vars"; | ||||
|  | ||||
| .extra-info { | ||||
|     display: flex; | ||||
|     margin-bottom: 0.5rem; | ||||
| } | ||||
|  | ||||
| .extra-info > div > div:first-child { | ||||
|     margin-left: 0 !important; | ||||
| } | ||||
|  | ||||
| .no-monitor-msg { | ||||
|     position: absolute; | ||||
|     width: 100%; | ||||
|   | ||||
| @@ -793,5 +793,7 @@ | ||||
|     "nostrRelaysHelp": "One relay URL per line", | ||||
|     "nostrSender": "Sender Private Key (nsec)", | ||||
|     "nostrRecipients": "Recipients Public Keys (npub)", | ||||
|     "nostrRecipientsHelp": "npub format, one per line" | ||||
|     "nostrRecipientsHelp": "npub format, one per line", | ||||
|     "showCertificateExpiry": "Show Certificate Expiry", | ||||
|     "noOrBadCertificate": "No/Bad Certificate" | ||||
| } | ||||
|   | ||||
| @@ -39,5 +39,6 @@ | ||||
|     "Reconnecting...": "पुनः कनेक्ट किया जा रहा है...", | ||||
|     "Down": "बंद", | ||||
|     "Passive Monitor Type": "निष्क्रिय मॉनिटर प्रकार", | ||||
|     "Status": "स्थिति" | ||||
|     "Status": "स्थिति", | ||||
|     "showCertificateExpiry": "प्रमाणपत्र समाप्ति दिखाएँ" | ||||
| } | ||||
|   | ||||
| @@ -54,6 +54,12 @@ | ||||
|                     <label class="form-check-label" for="show-powered-by">{{ $t("Show Powered By") }}</label> | ||||
|                 </div> | ||||
|  | ||||
|                 <!-- Show certificate expiry --> | ||||
|                 <div class="my-3 form-check form-switch"> | ||||
|                     <input id="show-certificate-expiry" v-model="config.showCertificateExpiry" class="form-check-input" type="checkbox"> | ||||
|                     <label class="form-check-label" for="show-certificate-expiry">{{ $t("showCertificateExpiry") }}</label> | ||||
|                 </div> | ||||
|  | ||||
|                 <div v-if="false" class="my-3"> | ||||
|                     <label for="password" class="form-label">{{ $t("Password") }} <sup>{{ $t("Coming Soon") }}</sup></label> | ||||
|                     <input id="password" v-model="config.password" disabled type="password" autocomplete="new-password" class="form-control"> | ||||
| @@ -309,7 +315,7 @@ | ||||
|                     👀 {{ $t("statusPageNothing") }} | ||||
|                 </div> | ||||
|  | ||||
|                 <PublicGroupList :edit-mode="enableEditMode" :show-tags="config.showTags" /> | ||||
|                 <PublicGroupList :edit-mode="enableEditMode" :show-tags="config.showTags" :show-certificate-expiry="config.showCertificateExpiry" /> | ||||
|             </div> | ||||
|  | ||||
|             <footer class="mt-5 mb-4"> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user