From b7ba6330dbf0e69308c1c37bd1637b1863c46f17 Mon Sep 17 00:00:00 2001 From: Nelson Chan <chakflying@hotmail.com> Date: Thu, 12 May 2022 18:18:47 +0800 Subject: [PATCH] Feat: Add cert exp. settings --- server/model/monitor.js | 19 ++++-- src/components/ActionInput.vue | 62 +++++++++++++++++++ src/components/settings/Notifications.vue | 72 ++++++++++++++++++++++- src/languages/en.js | 2 + src/pages/Settings.vue | 4 ++ 5 files changed, 153 insertions(+), 6 deletions(-) create mode 100644 src/components/ActionInput.vue diff --git a/server/model/monitor.js b/server/model/monitor.js index eaafb775..e1dc00e6 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -7,7 +7,7 @@ dayjs.extend(timezone); const axios = require("axios"); const { Prometheus } = require("../prometheus"); const { log, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util"); -const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mqttAsync } = require("../util-server"); +const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mqttAsync, setSetting } = require("../util-server"); const { R } = require("redbean-node"); const { BeanModel } = require("redbean-node/dist/bean-model"); const { Notification } = require("../notification"); @@ -826,10 +826,19 @@ class Monitor extends BeanModel { if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) { const notificationList = await Monitor.getNotificationList(this); - log.debug("monitor", "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); + let notifyDays = await setting("tlsExpiryNotifyDays"); + if (notifyDays == null || !Array.isArray(notifyDays)) { + // Reset Default + setSetting("tlsExpiryNotifyDays", [ 7, 14, 21 ], "general"); + notifyDays = [ 7, 14, 21 ]; + } + + if (notifyDays != null && Array.isArray(notifyDays)) { + for (const day of notifyDays) { + log.debug("monitor", "call sendCertNotificationByTargetDays", day); + await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, day, notificationList); + } + } } } diff --git a/src/components/ActionInput.vue b/src/components/ActionInput.vue new file mode 100644 index 00000000..00cb3aab --- /dev/null +++ b/src/components/ActionInput.vue @@ -0,0 +1,62 @@ +<template> + <div class="input-group mb-3"> + <input + ref="input" + v-model="model" + class="form-control" + :type="type" + :placeholder="placeholder" + :disabled="!enabled" + > + <a class="btn btn-outline-primary" @click="action()"> + <font-awesome-icon :icon="icon" /> + </a> + </div> +</template> + +<script> +export default { + props: { + modelValue: { + type: String, + default: "" + }, + enabled: { + type: Boolean, + default: true + }, + placeholder: { + type: String, + default: "" + }, + icon: { + type: String, + required: true, + }, + type: { + type: String, + default: "text", + }, + action: { + type: Function, + default: () => {}, + } + }, + emits: [ "update:modelValue" ], + computed: { + model: { + get() { + return this.modelValue; + }, + set(value) { + this.$emit("update:modelValue", value); + } + } + }, + created() { + + }, + methods: { + } +}; +</script> diff --git a/src/components/settings/Notifications.vue b/src/components/settings/Notifications.vue index b2cbcf48..187f4b7d 100644 --- a/src/components/settings/Notifications.vue +++ b/src/components/settings/Notifications.vue @@ -20,16 +20,74 @@ </button> </div> + <div class="my-4"> + <h4>{{ $t("settingsCertificateExpiry") }}</h4> + <p>{{ $t("certificationExpiryDescription") }}</p> + <div class="mt-2 mb-4 ps-2 cert-exp-days col-12 col-xl-6"> + <div v-for="day in settings.tlsExpiryNotifyDays" :key="day" class="d-flex align-items-center justify-content-between cert-exp-day-row py-2"> + <span>{{ day }} {{ $t("day(s)") }}</span> + <button type="button" class="btn btn-outline-primary ms-2 px-3 py-1" @click="removeExpiryNotifDay(day)"> + <font-awesome-icon class="" icon="times" /> + </button> + </div> + </div> + <div class="col-12 col-xl-6"> + <ActionInput v-model="expiryNotifInput" :type="'number'" :placeholder="$t('day(s)')" :icon="'plus'" :action="() => addExpiryNotifDay(expiryNotifInput)" /> + </div> + <div> + <button class="btn btn-primary" type="button" @click="saveSettings()"> + {{ $t("Save") }} + </button> + </div> + </div> + <NotificationDialog ref="notificationDialog" /> </div> </template> <script> import NotificationDialog from "../../components/NotificationDialog.vue"; +import ActionInput from "../ActionInput.vue"; export default { components: { - NotificationDialog + NotificationDialog, + ActionInput, + }, + + data() { + return { + expiryNotifInput: null, + }; + }, + + computed: { + settings() { + return this.$parent.$parent.$parent.settings; + }, + saveSettings() { + return this.$parent.$parent.$parent.saveSettings; + }, + settingsLoaded() { + return this.$parent.$parent.$parent.settingsLoaded; + }, + }, + + methods: { + removeExpiryNotifDay(day) { + this.settings.tlsExpiryNotifyDays = this.settings.tlsExpiryNotifyDays.filter(d => d !== day); + }, + addExpiryNotifDay(day) { + if (day != null && day !== "") { + const parsedDay = parseInt(day); + if (parsedDay != null && !isNaN(parsedDay) && parsedDay > 0) { + if (!this.settings.tlsExpiryNotifyDays.includes(parsedDay)) { + this.settings.tlsExpiryNotifyDays.push(parseInt(day)); + this.expiryNotifInput = null; + } + } + } + }, }, }; </script> @@ -43,4 +101,16 @@ export default { color: $dark-font-color; } } + +.cert-exp-days .cert-exp-day-row { + border-bottom: 1px solid rgba(0, 0, 0, 0.125); + + .dark & { + border-bottom: 1px solid $dark-border-color; + } +} + +.cert-exp-days .cert-exp-day-row:last-child { + border: none; +} </style> diff --git a/src/languages/en.js b/src/languages/en.js index af6ec32b..d21e40ef 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -515,4 +515,6 @@ export default { "Go back to the previous page.": "Go back to the previous page.", "Coming Soon": "Coming Soon", wayToGetClickSendSMSToken: "You can get API Username and API Key from {0} .", + settingsCertificateExpiry: "TLS Certificate Expiry", + certificationExpiryDescription: "HTTPS Monitors trigger notification when TLS certificate expires in:", }; diff --git a/src/pages/Settings.vue b/src/pages/Settings.vue index 11d3a1be..cb5f027c 100644 --- a/src/pages/Settings.vue +++ b/src/pages/Settings.vue @@ -145,6 +145,10 @@ export default { this.settings.keepDataPeriodDays = 180; } + if (this.settings.tlsExpiryNotifyDays === undefined) { + this.settings.tlsExpiryNotifyDays = []; + } + this.settingsLoaded = true; }); },