mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-24 08:32:23 +02:00
Merge pull request #3357 from tarun7singh/status-page-expiry
Status page certificate expiry
This commit is contained in:
commit
27ce47277b
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-add-invert-keyword.sql": true,
|
||||||
"patch-added-json-query.sql": true,
|
"patch-added-json-query.sql": true,
|
||||||
"patch-added-kafka-producer.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
|
* @param {boolean} [showTags=false] Should the JSON include monitor tags
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
async toPublicJSON(showTags = false) {
|
async toPublicJSON(showTags = false, certExpiry = false) {
|
||||||
let monitorBeanList = await this.getMonitorList();
|
let monitorBeanList = await this.getMonitorList();
|
||||||
let monitorList = [];
|
let monitorList = [];
|
||||||
|
|
||||||
for (let bean of monitorBeanList) {
|
for (let bean of monitorBeanList) {
|
||||||
monitorList.push(await bean.toPublicJSON(showTags));
|
monitorList.push(await bean.toPublicJSON(showTags, certExpiry));
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -37,11 +37,12 @@ class Monitor extends BeanModel {
|
|||||||
* Only show necessary data to public
|
* Only show necessary data to public
|
||||||
* @returns {Object}
|
* @returns {Object}
|
||||||
*/
|
*/
|
||||||
async toPublicJSON(showTags = false) {
|
async toPublicJSON(showTags = false, certExpiry = false) {
|
||||||
let obj = {
|
let obj = {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
name: this.name,
|
name: this.name,
|
||||||
sendUrl: this.sendUrl,
|
sendUrl: this.sendUrl,
|
||||||
|
type: this.type,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.sendUrl) {
|
if (this.sendUrl) {
|
||||||
@ -51,6 +52,13 @@ class Monitor extends BeanModel {
|
|||||||
if (showTags) {
|
if (showTags) {
|
||||||
obj.tags = await this.getTags();
|
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;
|
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 ]);
|
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
|
* Encode user and password to Base64 encoding
|
||||||
* for HTTP "basic" auth, as per RFC-7617
|
* for HTTP "basic" auth, as per RFC-7617
|
||||||
|
@ -90,6 +90,8 @@ class StatusPage extends BeanModel {
|
|||||||
* @param {StatusPage} statusPage
|
* @param {StatusPage} statusPage
|
||||||
*/
|
*/
|
||||||
static async getStatusPageData(statusPage) {
|
static async getStatusPageData(statusPage) {
|
||||||
|
const config = await statusPage.toPublicJSON();
|
||||||
|
|
||||||
// Incident
|
// Incident
|
||||||
let incident = await R.findOne("incident", " pin = 1 AND active = 1 AND status_page_id = ? ", [
|
let incident = await R.findOne("incident", " pin = 1 AND active = 1 AND status_page_id = ? ", [
|
||||||
statusPage.id,
|
statusPage.id,
|
||||||
@ -110,13 +112,13 @@ class StatusPage extends BeanModel {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
for (let groupBean of list) {
|
for (let groupBean of list) {
|
||||||
let monitorGroup = await groupBean.toPublicJSON(showTags);
|
let monitorGroup = await groupBean.toPublicJSON(showTags, config?.showCertificateExpiry);
|
||||||
publicGroupList.push(monitorGroup);
|
publicGroupList.push(monitorGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response
|
// Response
|
||||||
return {
|
return {
|
||||||
config: await statusPage.toPublicJSON(),
|
config,
|
||||||
incident,
|
incident,
|
||||||
publicGroupList,
|
publicGroupList,
|
||||||
maintenanceList,
|
maintenanceList,
|
||||||
@ -234,6 +236,7 @@ class StatusPage extends BeanModel {
|
|||||||
footerText: this.footer_text,
|
footerText: this.footer_text,
|
||||||
showPoweredBy: !!this.show_powered_by,
|
showPoweredBy: !!this.show_powered_by,
|
||||||
googleAnalyticsId: this.google_analytics_tag_id,
|
googleAnalyticsId: this.google_analytics_tag_id,
|
||||||
|
showCertificateExpiry: !!this.show_certificate_expiry,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,6 +258,7 @@ class StatusPage extends BeanModel {
|
|||||||
footerText: this.footer_text,
|
footerText: this.footer_text,
|
||||||
showPoweredBy: !!this.show_powered_by,
|
showPoweredBy: !!this.show_powered_by,
|
||||||
googleAnalyticsId: this.google_analytics_tag_id,
|
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.footer_text = config.footerText;
|
||||||
statusPage.custom_css = config.customCSS;
|
statusPage.custom_css = config.customCSS;
|
||||||
statusPage.show_powered_by = config.showPoweredBy;
|
statusPage.show_powered_by = config.showPoweredBy;
|
||||||
|
statusPage.show_certificate_expiry = config.showCertificateExpiry;
|
||||||
statusPage.modified_date = R.isoDateTime();
|
statusPage.modified_date = R.isoDateTime();
|
||||||
statusPage.google_analytics_tag_id = config.googleAnalyticsId;
|
statusPage.google_analytics_tag_id = config.googleAnalyticsId;
|
||||||
|
|
||||||
|
@ -61,8 +61,13 @@
|
|||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="showTags" class="tags">
|
<div class="extra-info">
|
||||||
<Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" />
|
<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>
|
</div>
|
||||||
<div :key="$root.userHeartbeatBar" class="col-3 col-md-4">
|
<div :key="$root.userHeartbeatBar" class="col-3 col-md-4">
|
||||||
@ -103,6 +108,10 @@ export default {
|
|||||||
/** Should tags be shown? */
|
/** Should tags be shown? */
|
||||||
showTags: {
|
showTags: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
},
|
||||||
|
/** Should expiry be shown? */
|
||||||
|
showCertificateExpiry: {
|
||||||
|
type: Boolean,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@ -154,6 +163,33 @@ export default {
|
|||||||
}
|
}
|
||||||
return monitor.element.sendUrl && monitor.element.url && monitor.element.url !== "https://" && !this.editMode;
|
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>
|
</script>
|
||||||
@ -161,6 +197,15 @@ export default {
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "../assets/vars";
|
@import "../assets/vars";
|
||||||
|
|
||||||
|
.extra-info {
|
||||||
|
display: flex;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.extra-info > div > div:first-child {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.no-monitor-msg {
|
.no-monitor-msg {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -793,5 +793,7 @@
|
|||||||
"nostrRelaysHelp": "One relay URL per line",
|
"nostrRelaysHelp": "One relay URL per line",
|
||||||
"nostrSender": "Sender Private Key (nsec)",
|
"nostrSender": "Sender Private Key (nsec)",
|
||||||
"nostrRecipients": "Recipients Public Keys (npub)",
|
"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...": "पुनः कनेक्ट किया जा रहा है...",
|
"Reconnecting...": "पुनः कनेक्ट किया जा रहा है...",
|
||||||
"Down": "बंद",
|
"Down": "बंद",
|
||||||
"Passive Monitor Type": "निष्क्रिय मॉनिटर प्रकार",
|
"Passive Monitor Type": "निष्क्रिय मॉनिटर प्रकार",
|
||||||
"Status": "स्थिति"
|
"Status": "स्थिति",
|
||||||
|
"showCertificateExpiry": "प्रमाणपत्र समाप्ति दिखाएँ"
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,12 @@
|
|||||||
<label class="form-check-label" for="show-powered-by">{{ $t("Show Powered By") }}</label>
|
<label class="form-check-label" for="show-powered-by">{{ $t("Show Powered By") }}</label>
|
||||||
</div>
|
</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">
|
<div v-if="false" class="my-3">
|
||||||
<label for="password" class="form-label">{{ $t("Password") }} <sup>{{ $t("Coming Soon") }}</sup></label>
|
<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">
|
<input id="password" v-model="config.password" disabled type="password" autocomplete="new-password" class="form-control">
|
||||||
@ -309,7 +315,7 @@
|
|||||||
👀 {{ $t("statusPageNothing") }}
|
👀 {{ $t("statusPageNothing") }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PublicGroupList :edit-mode="enableEditMode" :show-tags="config.showTags" />
|
<PublicGroupList :edit-mode="enableEditMode" :show-tags="config.showTags" :show-certificate-expiry="config.showCertificateExpiry" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer class="mt-5 mb-4">
|
<footer class="mt-5 mb-4">
|
||||||
|
Loading…
Reference in New Issue
Block a user