From 3d6c8b7f05f392adb9d6e2a409d67c5780afa640 Mon Sep 17 00:00:00 2001 From: Bert Verhelst <verhelstbert@gmail.com> Date: Fri, 1 Oct 2021 12:49:49 +0200 Subject: [PATCH 1/6] fix(heartbeat-bar): cleanup css styling and minor syntax issues --- src/assets/app.scss | 4 ++-- src/components/HeartbeatBar.vue | 23 +++++++++++++---------- src/components/MonitorList.vue | 24 ++++++++++++------------ src/components/Uptime.vue | 16 ++++++++-------- 4 files changed, 35 insertions(+), 32 deletions(-) diff --git a/src/assets/app.scss b/src/assets/app.scss index 34a4560..80ec08c 100644 --- a/src/assets/app.scss +++ b/src/assets/app.scss @@ -321,7 +321,7 @@ h2 { .item { display: block; text-decoration: none; - padding: 13px 15px 10px 15px; + padding: 10px 15px 10px 15px; border-radius: 10px; transition: all ease-in-out 0.15s; @@ -413,4 +413,4 @@ h2 { // Localization -@import "localization.scss"; \ No newline at end of file +@import "localization.scss"; diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue index 4dc2c71..459a4ad 100644 --- a/src/components/HeartbeatBar.vue +++ b/src/components/HeartbeatBar.vue @@ -38,7 +38,7 @@ export default { beatMargin: 4, move: false, maxBeat: -1, - } + }; }, computed: { @@ -69,12 +69,12 @@ export default { if (start < 0) { // Add empty placeholder for (let i = start; i < 0; i++) { - placeholders.push(0) + placeholders.push(0); } start = 0; } - return placeholders.concat(this.beatList.slice(start)) + return placeholders.concat(this.beatList.slice(start)); }, wrapStyle() { @@ -84,7 +84,7 @@ export default { return { padding: `${topBottom}px ${leftRight}px`, width: "100%", - } + }; }, barStyle() { @@ -94,12 +94,12 @@ export default { return { transition: "all ease-in-out 0.25s", transform: `translateX(${width}px)`, - } + }; } return { transform: "translateX(0)", - } + }; }, @@ -109,7 +109,7 @@ export default { height: this.beatHeight + "px", margin: this.beatMargin + "px", "--hover-scale": this.hoverScale, - } + }; }, }, @@ -120,7 +120,7 @@ export default { setTimeout(() => { this.move = false; - }, 300) + }, 300); }, deep: true, }, @@ -162,7 +162,7 @@ export default { methods: { resize() { if (this.$refs.wrap) { - this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatMargin * 2)) + this.maxBeat = Math.floor(this.$refs.wrap.clientWidth / (this.beatWidth + this.beatMargin * 2)); } }, @@ -170,7 +170,7 @@ export default { return `${this.$root.datetime(beat.time)} - ${beat.msg}`; } }, -} +}; </script> <style lang="scss" scoped> @@ -183,6 +183,9 @@ export default { } .hp-bar-big { + display: flex; + justify-content: flex-end; + .beat { display: inline-block; background-color: $primary; diff --git a/src/components/MonitorList.vue b/src/components/MonitorList.vue index fb3fcfb..54c5873 100644 --- a/src/components/MonitorList.vue +++ b/src/components/MonitorList.vue @@ -3,10 +3,10 @@ <div class="list-header"> <div class="placeholder"></div> <div class="search-wrapper"> - <a v-if="searchText == ''" class="search-icon"> + <a v-if="!searchText" class="search-icon"> <font-awesome-icon icon="search" /> </a> - <a v-if="searchText != ''" class="search-icon" @click="clearSearchText"> + <a v-if="searchText" class="search-icon" @click="clearSearchText"> <font-awesome-icon icon="times" /> </a> <input v-model="searchText" class="form-control search-input" :placeholder="$t('Search...')" /> @@ -19,21 +19,21 @@ <router-link v-for="(item, index) in sortedMonitorList" :key="index" :to="monitorURL(item.id)" class="item" :class="{ 'disabled': ! item.active }"> <div class="row"> - <div class="col-6 col-md-8 small-padding" :class="{ 'monitorItem': $root.userHeartbeatBar == 'bottom' || $root.userHeartbeatBar == 'none' }"> + <div class="col-6 col-md-8 small-padding" :class="{ 'monitorItem': $root.userHeartbeatBar === 'bottom' || $root.userHeartbeatBar === 'none' }"> <div class="info"> <Uptime :monitor="item" type="24" :pill="true" /> - {{ item.name }} + <span class="ms-1">{{ item.name }}</span> </div> <div class="tags"> <Tag v-for="tag in item.tags" :key="tag" :item="tag" :size="'sm'" /> </div> </div> - <div v-show="$root.userHeartbeatBar == 'normal'" :key="$root.userHeartbeatBar" class="col-6 col-md-4"> + <div v-show="$root.userHeartbeatBar === 'normal'" :key="$root.userHeartbeatBar" class="col-6 col-md-4 small-padding"> <HeartbeatBar size="small" :monitor-id="item.id" /> </div> </div> - <div v-if="$root.userHeartbeatBar == 'bottom'" class="row"> + <div v-if="$root.userHeartbeatBar === 'bottom'" class="row"> <div class="col-12"> <HeartbeatBar size="small" :monitor-id="item.id" /> </div> @@ -62,7 +62,7 @@ export default { data() { return { searchText: "", - } + }; }, computed: { sortedMonitorList() { @@ -91,17 +91,17 @@ export default { } return m1.name.localeCompare(m2.name); - }) + }); // Simple filter by search text // finds monitor name, tag name or tag value - if (this.searchText != "") { + if (this.searchText) { const loweredSearchText = this.searchText.toLowerCase(); result = result.filter(monitor => { return monitor.name.toLowerCase().includes(loweredSearchText) || monitor.tags.find(tag => tag.name.toLowerCase().includes(loweredSearchText) - || tag.value?.toLowerCase().includes(loweredSearchText)) - }) + || tag.value?.toLowerCase().includes(loweredSearchText)); + }); } return result; @@ -115,7 +115,7 @@ export default { this.searchText = ""; } }, -} +}; </script> <style lang="scss" scoped> diff --git a/src/components/Uptime.vue b/src/components/Uptime.vue index a4bf22f..a7931c1 100644 --- a/src/components/Uptime.vue +++ b/src/components/Uptime.vue @@ -22,33 +22,33 @@ export default { return Math.round(this.$root.uptimeList[key] * 10000) / 100 + "%"; } - return this.$t("notAvailableShort") + return this.$t("notAvailableShort"); }, color() { if (this.lastHeartBeat.status === 0) { - return "danger" + return "danger"; } if (this.lastHeartBeat.status === 1) { - return "primary" + return "primary"; } if (this.lastHeartBeat.status === 2) { - return "warning" + return "warning"; } - return "secondary" + return "secondary"; }, lastHeartBeat() { if (this.monitor.id in this.$root.lastHeartbeatList && this.$root.lastHeartbeatList[this.monitor.id]) { - return this.$root.lastHeartbeatList[this.monitor.id] + return this.$root.lastHeartbeatList[this.monitor.id]; } return { status: -1, - } + }; }, className() { @@ -59,7 +59,7 @@ export default { return ""; }, }, -} +}; </script> <style> From 13bdfefa9d36b299b1852b475f85793f007dfaef Mon Sep 17 00:00:00 2001 From: Nelson Chan <chakflying@hotmail.com> Date: Fri, 1 Oct 2021 18:44:32 +0800 Subject: [PATCH 2/6] Feat: Improve Certificaet Info Display --- server/prometheus.js | 2 +- server/util-server.js | 56 ++++++------ src/components/CertificateInfo.vue | 30 +++++++ src/components/CertificateInfoRow.vue | 106 +++++++++++++++++++++++ src/icon.js | 4 + src/mixins/socket.js | 4 +- src/pages/Details.vue | 118 ++++++++++---------------- 7 files changed, 216 insertions(+), 104 deletions(-) create mode 100644 src/components/CertificateInfo.vue create mode 100644 src/components/CertificateInfoRow.vue diff --git a/server/prometheus.js b/server/prometheus.js index 3e4767b..c27f87f 100644 --- a/server/prometheus.js +++ b/server/prometheus.js @@ -59,7 +59,7 @@ class Prometheus { } try { - monitor_cert_days_remaining.set(this.monitorLabelValues, tlsInfo.daysRemaining) + monitor_cert_days_remaining.set(this.monitorLabelValues, tlsInfo.certInfo.daysRemaining) } catch (e) { console.error(e) } diff --git a/server/util-server.js b/server/util-server.js index 29e4b11..66c50d8 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -185,38 +185,42 @@ const getDaysRemaining = (validFrom, validTo) => { 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) { - throw { - message: "No TLS certificate in response", - }; +// Fix certificate Info for display +// param: info - the chain obtained from getPeerCertificate() +const parseCertificateInfo = function (info) { + let link = info; + + while (link) { + if (!link.valid_from || !link.valid_to) { + break; + } + link.validTo = new Date(link.valid_to); + link.validFor = link.subjectaltname?.replace(/DNS:|IP Address:/g, "").split(", "); + link.daysRemaining = getDaysRemaining(new Date(), link.validTo); + + // Move up the chain until loop is encountered + if (link.issuerCertificate == null) { + break; + } else if (link.fingerprint == link.issuerCertificate.fingerprint) { + link.issuerCertificate = null; + break; + } else { + link = link.issuerCertificate; + } } - const valid = res.request.res.socket.authorized || false; - - const validTo = new Date(valid_to); + return info; +}; - const validFor = subjectaltname - .replace(/DNS:|IP Address:/g, "") - .split(", "); +exports.checkCertificate = function (res) { + const info = res.request.res.socket.getPeerCertificate(true); + const valid = res.request.res.socket.authorized || false; - const daysRemaining = getDaysRemaining(new Date(), validTo); + const parsedInfo = parseCertificateInfo(info); return { - valid, - validFor, - validTo, - daysRemaining, - issuer, - fingerprint, + valid: valid, + certInfo: parsedInfo }; }; diff --git a/src/components/CertificateInfo.vue b/src/components/CertificateInfo.vue new file mode 100644 index 0000000..0b23c95 --- /dev/null +++ b/src/components/CertificateInfo.vue @@ -0,0 +1,30 @@ +<template> + <div> + <h4>{{ $t("Certificate Info") }}</h4> + {{ $t("Certificate Chain") }}: + <div v-if="valid" class="rounded d-inline-flex ms-2 py-1 px-3 bg-success text-white">{{ $t("Valid") }}</div> + <div v-if="!valid" class="rounded d-inline-flex ms-2 py-1 px-3 bg-danger text-white">{{ $t("Invalid") }}</div> + <certificate-info-row :cert="certInfo" /> + </div> +</template> + +<script> +import CertificateInfoRow from "./CertificateInfoRow.vue"; +export default { + components: { + CertificateInfoRow, + }, + props: { + certInfo: { + type: Object, + required: true, + }, + valid: { + type: Boolean, + required: true, + }, + }, +}; +</script> + +<style></style> diff --git a/src/components/CertificateInfoRow.vue b/src/components/CertificateInfoRow.vue new file mode 100644 index 0000000..2b37d6e --- /dev/null +++ b/src/components/CertificateInfoRow.vue @@ -0,0 +1,106 @@ +<template> + <div> + <div class="d-flex flex-row align-items-center p-1 overflow-hidden"> + <div class="m-3 ps-3"> + <font-awesome-icon class="cert-icon" icon="file-contract" /> + </div> + <div class="m-3"> + <table class="text-start"> + <tbody> + <tr class="my-3"> + <td class="px-3">Subject:</td> + <td>{{ formatSubject(cert.subject) }}</td> + </tr> + <tr class="my-3"> + <td class="px-3">Valid To:</td> + <td><Datetime :value="cert.validTo" /></td> + </tr> + <tr class="my-3"> + <td class="px-3">Days Remaining:</td> + <td>{{ cert.daysRemaining }}</td> + </tr> + <tr class="my-3"> + <td class="px-3">Issuer:</td> + <td>{{ formatSubject(cert.issuer) }}</td> + </tr> + <tr class="my-3"> + <td class="px-3">Fingerprint:</td> + <td>{{ cert.fingerprint }}</td> + </tr> + </tbody> + </table> + </div> + </div> + <div class="d-flex"> + <font-awesome-icon + v-if="cert.issuerCertificate" + class="m-2 ps-6 link-icon" + icon="link" + /> + </div> + <certificate-info-row + v-if="cert.issuerCertificate" + :cert="cert.issuerCertificate" + /> + </div> +</template> + +<script> +import Datetime from "../components/Datetime.vue"; +export default { + name: "CertificateInfoRow", + components: { + Datetime, + }, + props: { + cert: { + type: Object, + required: true, + }, + }, + methods: { + formatSubject(subject) { + if (subject.O && subject.CN && subject.C) { + return `${subject.CN} - ${subject.O} (${subject.C})`; + } else if (subject.O && subject.CN) { + return `${subject.CN} - ${subject.O}`; + } else if (subject.CN) { + return subject.CN; + } else { + return "no info"; + } + }, + }, +}; +</script> + +<style lang="scss" scoped> +@import "../assets/vars.scss"; + +table { + overflow: hidden; +} + +.cert-icon { + font-size: 70px; + color: $link-color; + opacity: 0.5; + + .dark & { + color: $dark-font-color; + opacity: 0.3; + } +} + +.link-icon { + font-size: 20px; + margin-left: 50px !important; + color: $link-color; + opacity: 0.5; + + .dark & { + color: $dark-font-color; + opacity: 0.3; + } +} +</style> diff --git a/src/icon.js b/src/icon.js index 6fb9149..5ac3511 100644 --- a/src/icon.js +++ b/src/icon.js @@ -30,6 +30,8 @@ import { faUpload, faCopy, faCheck, + faFileContract, + faLink, } from "@fortawesome/free-solid-svg-icons"; library.add( @@ -59,6 +61,8 @@ library.add( faUpload, faCopy, faCheck, + faFileContract, + faLink, ); export { FontAwesomeIcon }; diff --git a/src/mixins/socket.js b/src/mixins/socket.js index a14771c..8a12ae4 100644 --- a/src/mixins/socket.js +++ b/src/mixins/socket.js @@ -30,7 +30,7 @@ export default { importantHeartbeatList: { }, avgPingList: { }, uptimeList: { }, - certInfoList: {}, + tlsInfoList: {}, notificationList: [], connectionErrorMsg: "Cannot connect to the socket server. Reconnecting...", }; @@ -154,7 +154,7 @@ export default { }); socket.on("certInfo", (monitorID, data) => { - this.certInfoList[monitorID] = JSON.parse(data); + this.tlsInfoList[monitorID] = JSON.parse(data); }); socket.on("importantHeartbeatList", (monitorID, data, overwrite) => { diff --git a/src/pages/Details.vue b/src/pages/Details.vue index e4aeb28..ee0c494 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -73,11 +73,11 @@ <span class="num"><Uptime :monitor="monitor" type="720" /></span> </div> - <div v-if="certInfo" class="col"> + <div v-if="tlsInfo" class="col"> <h4>{{ $t("Cert Exp.") }}</h4> - <p>(<Datetime :value="certInfo.validTo" date-only />)</p> + <p>(<Datetime :value="tlsInfo.certInfo.validTo" date-only />)</p> <span class="num"> - <a href="#" @click.prevent="toggleCertInfoBox = !toggleCertInfoBox">{{ certInfo.daysRemaining }} {{ $t("days") }}</a> + <a href="#" @click.prevent="toggleCertInfoBox = !toggleCertInfoBox">{{ tlsInfo.certInfo.daysRemaining }} {{ $t("days") }}</a> </span> </div> </div> @@ -87,41 +87,7 @@ <div v-if="showCertInfoBox" class="shadow-box big-padding text-center"> <div class="row"> <div class="col"> - <h4>{{ $t("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><Datetime :value="certInfo.validTo" /></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> + <certificate-info :certInfo="tlsInfo.certInfo" :valid="tlsInfo.valid" /> </div> </div> </div> @@ -207,8 +173,8 @@ <script> import { defineAsyncComponent } from "vue"; -import { useToast } from "vue-toastification" -const toast = useToast() +import { useToast } from "vue-toastification"; +const toast = useToast(); import Confirm from "../components/Confirm.vue"; import HeartbeatBar from "../components/HeartbeatBar.vue"; import Status from "../components/Status.vue"; @@ -218,6 +184,7 @@ import Uptime from "../components/Uptime.vue"; import Pagination from "v-pagination-3"; const PingChart = defineAsyncComponent(() => import("../components/PingChart.vue")); import Tag from "../components/Tag.vue"; +import CertificateInfo from "../components/CertificateInfo.vue"; export default { components: { @@ -230,6 +197,7 @@ export default { Pagination, PingChart, Tag, + CertificateInfo, }, data() { return { @@ -239,32 +207,32 @@ export default { toggleCertInfoBox: false, showPingChartBox: true, paginationConfig: { - texts:{ - count:`${this.$t("Showing {from} to {to} of {count} records")}|{count} ${this.$t("records")}|${this.$t("One record")}`, - first:this.$t("First"), - last:this.$t("Last"), - nextPage:'>', - nextChunk:'>>', - prevPage:'<', - prevChunk:'<<' + texts: { + count: `${this.$t("Showing {from} to {to} of {count} records")}|{count} ${this.$t("records")}|${this.$t("One record")}`, + first: this.$t("First"), + last: this.$t("Last"), + nextPage: ">", + nextChunk: ">>", + prevPage: "<", + prevChunk: "<<" } } - } + }; }, computed: { monitor() { - let id = this.$route.params.id + let id = this.$route.params.id; return this.$root.monitorList[id]; }, lastHeartBeat() { if (this.monitor.id in this.$root.lastHeartbeatList && this.$root.lastHeartbeatList[this.monitor.id]) { - return this.$root.lastHeartbeatList[this.monitor.id] + return this.$root.lastHeartbeatList[this.monitor.id]; } return { status: -1, - } + }; }, ping() { @@ -272,7 +240,7 @@ export default { return this.lastHeartBeat.ping; } - return this.$t("notAvailableShort") + return this.$t("notAvailableShort"); }, avgPing() { @@ -280,14 +248,14 @@ export default { return this.$root.avgPingList[this.monitor.id]; } - return this.$t("notAvailableShort") + return this.$t("notAvailableShort"); }, importantHeartBeatList() { if (this.$root.importantHeartbeatList[this.monitor.id]) { // eslint-disable-next-line vue/no-side-effects-in-computed-properties this.heartBeatList = this.$root.importantHeartbeatList[this.monitor.id]; - return this.$root.importantHeartbeatList[this.monitor.id] + return this.$root.importantHeartbeatList[this.monitor.id]; } return []; @@ -295,22 +263,22 @@ export default { status() { if (this.$root.statusList[this.monitor.id]) { - return this.$root.statusList[this.monitor.id] + return this.$root.statusList[this.monitor.id]; } - return { } + return { }; }, - certInfo() { - if (this.$root.certInfoList[this.monitor.id]) { - return this.$root.certInfoList[this.monitor.id] + tlsInfo() { + if (this.$root.tlsInfoList[this.monitor.id]) { + return this.$root.tlsInfoList[this.monitor.id]; } - return null + return null; }, showCertInfoBox() { - return this.certInfo != null && this.toggleCertInfoBox; + return this.tlsInfo != null && this.toggleCertInfoBox; }, displayedRecords() { @@ -324,8 +292,8 @@ export default { }, methods: { testNotification() { - this.$root.getSocket().emit("testNotification", this.monitor.id) - toast.success("Test notification is requested.") + this.$root.getSocket().emit("testNotification", this.monitor.id); + toast.success("Test notification is requested."); }, pauseDialog() { @@ -334,14 +302,14 @@ export default { resumeMonitor() { this.$root.getSocket().emit("resumeMonitor", this.monitor.id, (res) => { - this.$root.toastRes(res) - }) + this.$root.toastRes(res); + }); }, pauseMonitor() { this.$root.getSocket().emit("pauseMonitor", this.monitor.id, (res) => { - this.$root.toastRes(res) - }) + this.$root.toastRes(res); + }); }, deleteDialog() { @@ -360,11 +328,11 @@ export default { this.$root.deleteMonitor(this.monitor.id, (res) => { if (res.ok) { toast.success(res.msg); - this.$router.push("/dashboard") + this.$router.push("/dashboard"); } else { toast.error(res.msg); } - }) + }); }, clearEvents() { @@ -372,7 +340,7 @@ export default { if (! res.ok) { toast.error(res.msg); } - }) + }); }, clearHeartbeats() { @@ -380,13 +348,13 @@ export default { if (! res.ok) { toast.error(res.msg); } - }) + }); }, pingTitle(average = false) { - let translationPrefix = "" + let translationPrefix = ""; if (average) { - translationPrefix = "Avg. " + translationPrefix = "Avg. "; } if (this.monitor.type === "http") { @@ -396,7 +364,7 @@ export default { return this.$t(translationPrefix + "Ping"); }, }, -} +}; </script> <style lang="scss" scoped> From 1c2adf8723e6664501a31d03a376f35030631da6 Mon Sep 17 00:00:00 2001 From: Bert Verhelst <verhelstbert@gmail.com> Date: Fri, 1 Oct 2021 15:43:20 +0200 Subject: [PATCH 3/6] fix(monitor-list): increase padding to keep same monitor item height --- src/assets/app.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assets/app.scss b/src/assets/app.scss index 80ec08c..7e4fc07 100644 --- a/src/assets/app.scss +++ b/src/assets/app.scss @@ -321,7 +321,7 @@ h2 { .item { display: block; text-decoration: none; - padding: 10px 15px 10px 15px; + padding: 14px 15px; border-radius: 10px; transition: all ease-in-out 0.15s; From b7568e9caa14400d6972b892278eb4398a629b81 Mon Sep 17 00:00:00 2001 From: Nelson Chan <chakflying@hotmail.com> Date: Fri, 1 Oct 2021 22:29:22 +0800 Subject: [PATCH 4/6] Fix: Update Certificate Icon --- src/components/CertificateInfoRow.vue | 18 +++++++++++++++++- src/icon.js | 6 ++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/components/CertificateInfoRow.vue b/src/components/CertificateInfoRow.vue index 2b37d6e..df726eb 100644 --- a/src/components/CertificateInfoRow.vue +++ b/src/components/CertificateInfoRow.vue @@ -2,7 +2,10 @@ <div> <div class="d-flex flex-row align-items-center p-1 overflow-hidden"> <div class="m-3 ps-3"> - <font-awesome-icon class="cert-icon" icon="file-contract" /> + <div class="cert-icon"> + <font-awesome-icon icon="file" /> + <font-awesome-icon class="award-icon" icon="award" /> + </div> </div> <div class="m-3"> <table class="text-start"> @@ -82,6 +85,7 @@ table { } .cert-icon { + position: relative; font-size: 70px; color: $link-color; opacity: 0.5; @@ -92,6 +96,18 @@ table { } } +.award-icon { + position: absolute; + font-size: 0.5em; + bottom: 20%; + left: 12%; + color: white; + + .dark & { + color: $dark-bg; + } +} + .link-icon { font-size: 20px; margin-left: 50px !important; diff --git a/src/icon.js b/src/icon.js index 5ac3511..e78992f 100644 --- a/src/icon.js +++ b/src/icon.js @@ -30,7 +30,8 @@ import { faUpload, faCopy, faCheck, - faFileContract, + faFile, + faAward, faLink, } from "@fortawesome/free-solid-svg-icons"; @@ -61,7 +62,8 @@ library.add( faUpload, faCopy, faCheck, - faFileContract, + faFile, + faAward, faLink, ); From 668fd58af37318c2cdbbe0f324821280ea3efcc6 Mon Sep 17 00:00:00 2001 From: Nelson Chan <chakflying@hotmail.com> Date: Fri, 1 Oct 2021 22:43:09 +0800 Subject: [PATCH 5/6] Fix: Slightly improve validity styling --- src/components/CertificateInfo.vue | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/components/CertificateInfo.vue b/src/components/CertificateInfo.vue index 0b23c95..bb10f15 100644 --- a/src/components/CertificateInfo.vue +++ b/src/components/CertificateInfo.vue @@ -2,8 +2,18 @@ <div> <h4>{{ $t("Certificate Info") }}</h4> {{ $t("Certificate Chain") }}: - <div v-if="valid" class="rounded d-inline-flex ms-2 py-1 px-3 bg-success text-white">{{ $t("Valid") }}</div> - <div v-if="!valid" class="rounded d-inline-flex ms-2 py-1 px-3 bg-danger text-white">{{ $t("Invalid") }}</div> + <div + v-if="valid" + class="rounded d-inline-flex ms-2 text-white tag-valid" + > + {{ $t("Valid") }} + </div> + <div + v-if="!valid" + class="rounded d-inline-flex ms-2 text-white tag-invalid" + > + {{ $t("Invalid") }} + </div> <certificate-info-row :cert="certInfo" /> </div> </template> @@ -27,4 +37,16 @@ export default { }; </script> -<style></style> +<style lang="scss" scoped> +@import "../assets/vars.scss"; + +.tag-valid { + padding: 2px 25px; + background-color: $primary; +} + +.tag-invalid { + padding: 2px 25px; + background-color: $danger; +} +</style> From 259bcf94268ac088b5732544927e5aac1431900e Mon Sep 17 00:00:00 2001 From: LouisLam <louislam@users.noreply.github.com> Date: Tue, 5 Oct 2021 15:57:13 +0800 Subject: [PATCH 6/6] [SMTP] "To Email" is not required if CC/BCC is set. (#461) --- package-lock.json | 615 +++++++++++++++++++++++++- src/components/notifications/SMTP.vue | 19 +- 2 files changed, 626 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index ca515d7..05d2608 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,14 +62,16 @@ "@vitejs/plugin-legacy": "~1.5.3", "@vitejs/plugin-vue": "~1.9.1", "@vue/compiler-sfc": "~3.2.16", + "@vue/test-utils": "^2.0.0-rc.15", + "@vue/vue3-jest": "^27.0.0-alpha.1", "core-js": "~3.18.0", "cross-env": "~7.0.3", "dns2": "~2.0.1", "eslint": "~7.32.0", "eslint-plugin-vue": "~7.18.0", - "jest": "^27.2.4", - "jest-puppeteer": "^6.0.0", - "puppeteer": "^10.4.0", + "jest": "~27.2.4", + "jest-puppeteer": "~6.0.0", + "puppeteer": "~10.4.0", "sass": "~1.42.1", "stylelint": "~13.13.1", "stylelint-config-standard": "~22.0.0", @@ -639,6 +641,24 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.15.4.tgz", + "integrity": "sha512-qg4DPhwG8hKp4BbVDvX1s8cohM8a6Bvptu4l6Iingq5rW+yRUAhe/YRup/YcW2zCOlrysEWVhftIcKzrEZv3sA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-simple-access": "^7.15.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/standalone": { "version": "7.15.7", "resolved": "https://registry.npmjs.org/@babel/standalone/-/standalone-7.15.7.tgz", @@ -1653,6 +1673,18 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, + "node_modules/@types/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", + "dev": true + }, + "node_modules/@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "dev": true + }, "node_modules/@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", @@ -1822,6 +1854,125 @@ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.19.tgz", "integrity": "sha512-Knqhx7WieLdVgwCAZgTVrDCXZ50uItuecLh9JdLC8O+a5ayaSyIQYveUK3hCRNC7ws5zalHmZwfdLMGaS8r4Ew==" }, + "node_modules/@vue/test-utils": { + "version": "2.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.0.0-rc.15.tgz", + "integrity": "sha512-cb+Ri4PDRhtGCJuaLyl1HO9jXcwEj6AFwcNXace8FhhwelDzOdjyIgOb25xtDiUojzWjPuzGLKZQr/5WB7MLew==", + "dev": true, + "peerDependencies": { + "vue": "^3.0.1" + } + }, + "node_modules/@vue/vue3-jest": { + "version": "27.0.0-alpha.1", + "resolved": "https://registry.npmjs.org/@vue/vue3-jest/-/vue3-jest-27.0.0-alpha.1.tgz", + "integrity": "sha512-V4erTP0LvI0B4kM/cgSiusF0yahByrqJCAUQKDvpW3104J4njoNUY1HwSMqvv5SASOrzxer4ui3EDWyl8Pw2Lg==", + "dev": true, + "dependencies": { + "@babel/plugin-transform-modules-commonjs": "^7.2.0", + "chalk": "^2.1.0", + "convert-source-map": "^1.6.0", + "extract-from-css": "^0.4.4", + "source-map": "0.5.6", + "tsconfig": "^7.0.0" + }, + "peerDependencies": { + "@babel/core": "7.x", + "babel-jest": "27.x", + "jest": "27.x", + "ts-jest": "27.x", + "typescript": ">= 3.x", + "vue": "^3.0.0-0" + }, + "peerDependenciesMeta": { + "ts-jest": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@vue/vue3-jest/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@vue/vue3-jest/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@vue/vue3-jest/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@vue/vue3-jest/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/@vue/vue3-jest/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@vue/vue3-jest/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@vue/vue3-jest/node_modules/source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@vue/vue3-jest/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/abab": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", @@ -2076,6 +2227,18 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, "node_modules/autoprefixer": { "version": "9.8.7", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.7.tgz", @@ -2170,6 +2333,15 @@ "resolved": "https://registry.npmjs.org/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-0.2.1.tgz", "integrity": "sha1-mumh9KjcZ/DN7E9K7aHkOl/2XiU=" }, + "node_modules/babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "dependencies": { + "object.assign": "^4.1.0" + } + }, "node_modules/babel-plugin-istanbul": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", @@ -2565,6 +2737,19 @@ "node": ">= 0.8" } }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -3026,6 +3211,18 @@ "node": ">= 8" } }, + "node_modules/css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -3198,6 +3395,15 @@ "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", "dev": true }, + "node_modules/decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, "node_modules/dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -3219,6 +3425,18 @@ "node": ">=0.10.0" } }, + "node_modules/define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -4072,6 +4290,19 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "devOptional": true }, + "node_modules/extract-from-css": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/extract-from-css/-/extract-from-css-0.4.4.tgz", + "integrity": "sha1-HqffLnx8brmSL6COitrqSG9vj5I=", + "dev": true, + "dependencies": { + "css": "^2.1.0" + }, + "engines": { + "node": ">=0.10.0", + "npm": ">=2.0.0" + } + }, "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", @@ -4502,6 +4733,20 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -4745,6 +4990,18 @@ "node": ">=8" } }, + "node_modules/has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -7362,6 +7619,33 @@ "node": ">=0.10.0" } }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -8520,6 +8804,13 @@ "node": ">=4" } }, + "node_modules/resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "deprecated": "https://github.com/lydell/resolve-url#deprecated", + "dev": true + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -8966,6 +9257,19 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, "node_modules/source-map-support": { "version": "0.5.20", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", @@ -8976,6 +9280,12 @@ "source-map": "^0.6.0" } }, + "node_modules/source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "dev": true + }, "node_modules/sourcemap-codec": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", @@ -9757,6 +10067,36 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/tsconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", + "dev": true, + "dependencies": { + "@types/strip-bom": "^3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + } + }, + "node_modules/tsconfig/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tsconfig/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/tslib": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", @@ -9946,6 +10286,13 @@ "punycode": "^2.1.0" } }, + "node_modules/urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "deprecated": "Please see https://github.com/lydell/urix#deprecated", + "dev": true + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -11167,6 +11514,18 @@ "@babel/helper-plugin-utils": "^7.14.5" } }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.15.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.15.4.tgz", + "integrity": "sha512-qg4DPhwG8hKp4BbVDvX1s8cohM8a6Bvptu4l6Iingq5rW+yRUAhe/YRup/YcW2zCOlrysEWVhftIcKzrEZv3sA==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.15.4", + "@babel/helper-plugin-utils": "^7.14.5", + "@babel/helper-simple-access": "^7.15.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, "@babel/standalone": { "version": "7.15.7", "resolved": "https://registry.npmjs.org/@babel/standalone/-/standalone-7.15.7.tgz", @@ -12020,6 +12379,18 @@ "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", "dev": true }, + "@types/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=", + "dev": true + }, + "@types/strip-json-comments": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz", + "integrity": "sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==", + "dev": true + }, "@types/unist": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz", @@ -12175,6 +12546,91 @@ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.19.tgz", "integrity": "sha512-Knqhx7WieLdVgwCAZgTVrDCXZ50uItuecLh9JdLC8O+a5ayaSyIQYveUK3hCRNC7ws5zalHmZwfdLMGaS8r4Ew==" }, + "@vue/test-utils": { + "version": "2.0.0-rc.15", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.0.0-rc.15.tgz", + "integrity": "sha512-cb+Ri4PDRhtGCJuaLyl1HO9jXcwEj6AFwcNXace8FhhwelDzOdjyIgOb25xtDiUojzWjPuzGLKZQr/5WB7MLew==", + "dev": true, + "requires": {} + }, + "@vue/vue3-jest": { + "version": "27.0.0-alpha.1", + "resolved": "https://registry.npmjs.org/@vue/vue3-jest/-/vue3-jest-27.0.0-alpha.1.tgz", + "integrity": "sha512-V4erTP0LvI0B4kM/cgSiusF0yahByrqJCAUQKDvpW3104J4njoNUY1HwSMqvv5SASOrzxer4ui3EDWyl8Pw2Lg==", + "dev": true, + "requires": { + "@babel/plugin-transform-modules-commonjs": "^7.2.0", + "chalk": "^2.1.0", + "convert-source-map": "^1.6.0", + "extract-from-css": "^0.4.4", + "source-map": "0.5.6", + "tsconfig": "^7.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "abab": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz", @@ -12368,6 +12824,12 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, "autoprefixer": { "version": "9.8.7", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.7.tgz", @@ -12441,6 +12903,15 @@ "resolved": "https://registry.npmjs.org/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-0.2.1.tgz", "integrity": "sha1-mumh9KjcZ/DN7E9K7aHkOl/2XiU=" }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, "babel-plugin-istanbul": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", @@ -12752,6 +13223,16 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -13100,6 +13581,18 @@ "which": "^2.0.1" } }, + "css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + } + }, "cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -13234,6 +13727,12 @@ "integrity": "sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==", "dev": true }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, "dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -13252,6 +13751,15 @@ "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", "dev": true }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -13927,6 +14435,15 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "devOptional": true }, + "extract-from-css": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/extract-from-css/-/extract-from-css-0.4.4.tgz", + "integrity": "sha1-HqffLnx8brmSL6COitrqSG9vj5I=", + "dev": true, + "requires": { + "css": "^2.1.0" + } + }, "extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", @@ -14261,6 +14778,17 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -14438,6 +14966,12 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, + "has-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", + "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", + "dev": true + }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -16426,6 +16960,24 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, "on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", @@ -17302,6 +17854,12 @@ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -17640,6 +18198,19 @@ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz", "integrity": "sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==" }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, "source-map-support": { "version": "0.5.20", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", @@ -17650,6 +18221,12 @@ "source-map": "^0.6.0" } }, + "source-map-url": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz", + "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==", + "dev": true + }, "sourcemap-codec": { "version": "1.4.8", "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", @@ -18280,6 +18857,32 @@ "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", "dev": true }, + "tsconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz", + "integrity": "sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==", + "dev": true, + "requires": { + "@types/strip-bom": "^3.0.0", + "@types/strip-json-comments": "0.0.30", + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + } + } + }, "tslib": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", @@ -18421,6 +19024,12 @@ "punycode": "^2.1.0" } }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/src/components/notifications/SMTP.vue b/src/components/notifications/SMTP.vue index b86a626..0656b13 100644 --- a/src/components/notifications/SMTP.vue +++ b/src/components/notifications/SMTP.vue @@ -45,17 +45,17 @@ <div class="mb-3"> <label for="to-email" class="form-label">To Email</label> - <input id="to-email" v-model="$parent.notification.smtpTo" type="text" class="form-control" required autocomplete="false" placeholder="example2@kuma.pet, example3@kuma.pet"> + <input id="to-email" v-model="$parent.notification.smtpTo" type="text" class="form-control" autocomplete="false" placeholder="example2@kuma.pet, example3@kuma.pet" :required="!hasRecipient"> </div> <div class="mb-3"> <label for="to-cc" class="form-label">CC</label> - <input id="to-cc" v-model="$parent.notification.smtpCC" type="text" class="form-control" autocomplete="false"> + <input id="to-cc" v-model="$parent.notification.smtpCC" type="text" class="form-control" autocomplete="false" :required="!hasRecipient"> </div> <div class="mb-3"> <label for="to-bcc" class="form-label">BCC</label> - <input id="to-bcc" v-model="$parent.notification.smtpBCC" type="text" class="form-control" autocomplete="false"> + <input id="to-bcc" v-model="$parent.notification.smtpBCC" type="text" class="form-control" autocomplete="false" :required="!hasRecipient"> </div> </template> @@ -66,10 +66,19 @@ export default { components: { HiddenInput, }, + computed: { + hasRecipient() { + if (this.$parent.notification.smtpTo || this.$parent.notification.smtpCC || this.$parent.notification.smtpBCC) { + return true; + } else { + return false; + } + } + }, mounted() { if (typeof this.$parent.notification.smtpSecure === "undefined") { this.$parent.notification.smtpSecure = false; } - }, -} + } +}; </script>