Adds domain name monitor widget

This commit is contained in:
Alicia Sykes 2022-05-28 14:43:26 +01:00
parent c8010f50a7
commit 2d49f1cda4
5 changed files with 255 additions and 5 deletions

View File

@ -0,0 +1,238 @@
<template>
<div class="blacklist-check-wrapper">
<!-- Domain Name and Registration State / Expiry Count Down -->
<div class="expiry-wrap" v-if="domainMeta" @click="toggleDomainInfo">
<span class="name">{{ domainMeta.domainName }}</span>
<span v-if="!domainMeta.isRegistered" class="not-registered">
Not Registered
</span>
<span v-if="domainMeta.isRegistered"
:class="`is-registered expire-date ${ getExpireColor(domainRegistration.expireDate) }`">
{{ domainRegistration.expireDate | formatDate }}
</span>
<span v-if="domainMeta.isRegistered"
:class="`is-registered time-left ${getExpireColor(domainRegistration.expireDate) }`">
{{ domainRegistration.expireDate | formatTimeLeft }}
</span>
</div>
<!-- Domain Info -->
<div v-if="showDomainInfo && domainRegistration" class="domain-more-data">
<div class="row">
<span class="lbl">Created</span>
<span class="val">{{ domainRegistration.createdDate | formatDate }}</span>
</div>
<div class="row">
<span class="lbl">Updated</span>
<span class="val">{{ domainRegistration.updatedDate | formatDate }}</span>
</div>
<div class="row">
<span class="lbl">Expires</span>
<span class="val">{{ domainRegistration.expireDate | formatDate }}</span>
</div>
<div class="row" v-for="(ns, inx) in domainRegistration.nameServers" :key="inx">
<span class="lbl">NS {{ inx + 1 }}</span>
<span class="val">{{ ns }}</span>
</div>
<div class="row">
<span class="lbl">Domain ID</span>
<span class="val">{{ domainRegistration.domainId }}</span>
</div>
<div class="row" v-if="domainRegistration.registrar">
<span class="lbl">Registrar</span>
<span class="val">{{ domainRegistration.registrar }}</span>
</div>
<div class="row" v-if="domainRegistration.admin">
<span class="lbl">Admin</span>
<span class="val">{{ domainRegistration.admin }}</span>
</div>
</div>
<!-- Toggle Button -->
<p @click="toggleDomainInfo" class="expend-details-btn" v-if="domainRegistration">
{{ showDomainInfo ? $t('widgets.general.show-less') : $t('widgets.general.show-more') }}
</p>
</div>
</template>
<script>
import WidgetMixin from '@/mixins/WidgetMixin';
import { timestampToDate, getTimeAgo } from '@/utils/MiscHelpers';
import { widgetApiEndpoints } from '@/utils/defaults';
export default {
mixins: [WidgetMixin],
computed: {
apiKey() {
if (!this.options.apiKey) this.error('Missing API Key');
return this.options.apiKey;
},
domain() {
if (!this.options.domain) this.error('Missing Domain Name Key');
return this.options.domain;
},
endpoint() {
return `${widgetApiEndpoints.domainMonitor}/?domain=${this.domain}&r=whois&apikey=${this.apiKey}`;
},
},
data() {
return {
domainMeta: null,
domainRegistration: null,
showDomainInfo: false,
};
},
filters: {
formatDate(date) {
if (!date) return 'No Date Supplied';
return timestampToDate(date);
},
formatTimeLeft(date) {
return getTimeAgo(new Date(date)).replace('in', '');
},
},
methods: {
/* Make GET request to CoinGecko API endpoint */
fetchData() {
this.makeRequest(this.endpoint).then(this.processData);
},
/* Assign data variables to the returned data */
processData(domainResults) {
if (domainResults.limit_hit) this.error('API Limit Reached');
if (domainResults.status !== '0') this.error(domainResults.status_desc || 'API Error');
// Get domain name and registration status
const domainName = domainResults.domain_name;
const isRegistered = domainResults.registered;
this.domainMeta = { domainName, isRegistered };
// If domain registered, get registration info and expiry dates
if (isRegistered) {
this.domainRegistration = {
expireDate: domainResults.date_expires,
createdDate: domainResults.date_created,
updatedDate: domainResults.date_updated,
nameServers: domainResults.nameservers,
domainId: domainResults.registry_domain_id,
registrar: this.getRegistrar(domainResults.contacts),
admin: this.getAdmin(domainResults.contacts),
};
}
},
getExpireColor(targetDate) {
const now = new Date().getTime();
const then = new Date(targetDate).getTime();
const diff = Math.round((then - now) / (1000 * 60 * 60 * 24));
if (diff < 7) return 'red';
if (diff < 30) return 'orange';
if (diff < 180) return 'yellow';
if (diff >= 180) return 'green';
return 'grey';
},
getRegistrar(contacts) {
if (!Array.isArray(contacts) || contacts.length < 1) return null;
const registrar = contacts.find((contact) => contact.type === 'registrar');
if (registrar) return registrar.name || registrar.organization;
return null;
},
getAdmin(contacts) {
if (!Array.isArray(contacts) || contacts.length < 1) return null;
const accHolder = contacts.find((contact) => contact.type === 'admin')
|| contacts.find((contact) => contact.type === 'registrant');
if (accHolder) return accHolder.name || accHolder.organization;
return null;
},
/* Show / hide full domain info */
toggleDomainInfo() {
this.showDomainInfo = !this.showDomainInfo;
},
},
mounted() {
if (this.options.showFullInfo) this.showDomainInfo = true;
},
};
</script>
<style scoped lang="scss">
.blacklist-check-wrapper {
color: var(--widget-text-color);
padding: 0.25rem;
cursor: default;
overflow: auto;
}
.expiry-wrap {
display: flex;
align-items: center;
justify-content: space-between;
color: var(--widget-text-color);
cursor: default;
font-size: 1.2rem;
font-weight: bold;
span.name {
max-width: 50%;
overflow: hidden;
text-overflow: ellipsis;
}
span.not-registered {
color: var(--info);
}
span.expire-date {
display: none;
white-space: pre;
}
span.expire-date, span.time-left {
&.red { color: var(--danger); }
&.orange { color: var(--error); }
&.yellow { color: var(--warning); }
&.green { color: var(--success); }
&.grey { color: var(--neutral); }
&.blue { color: var(--info); }
}
}
.blacklist-check-wrapper {
&:hover {
.expend-details-btn {
visibility: visible;
}
span.expire-date {
display: block;
}
span.time-left {
display: none;
}
}
}
.expend-details-btn {
visibility: hidden;
margin: 0.2rem;
font-size: 0.8rem;
text-align: center;
opacity: var(--dimming-factor);
cursor: pointer;
}
.domain-more-data {
display: flex;
flex-direction: column;
margin: 0.5rem 0;
.row {
display: flex;
padding: 0.2rem 0;
justify-content: space-between;
opacity: var(--dimming-factor);
color: var(--widget-text-color);
&:not(:last-child) { border-bottom: 1px dashed var(--widget-text-color); }
span.lbl {}
span.val {
font-family: var(--font-monospace);
max-width: 70%;
overflow: hidden;
text-overflow: ellipsis;
white-space: pre;
&:hover {
max-width: 100%;
}
}
}
}
</style>

View File

@ -69,6 +69,13 @@
@error="handleError"
:ref="widgetRef"
/>
<DomainMonitor
v-else-if="widgetType === 'domain-monitor'"
:options="widgetOptions"
@loading="setLoaderState"
@error="handleError"
:ref="widgetRef"
/>
<CodeStats
v-else-if="widgetType === 'code-stats'"
:options="widgetOptions"
@ -430,6 +437,7 @@ export default {
CryptoPriceChart: () => import('@/components/Widgets/CryptoPriceChart.vue'),
CryptoWatchList: () => import('@/components/Widgets/CryptoWatchList.vue'),
CveVulnerabilities: () => import('@/components/Widgets/CveVulnerabilities.vue'),
DomainMonitor: () => import('@/components/Widgets/DomainMonitor.vue'),
EmbedWidget: () => import('@/components/Widgets/EmbedWidget.vue'),
EthGasPrices: () => import('@/components/Widgets/EthGasPrices.vue'),
ExchangeRates: () => import('@/components/Widgets/ExchangeRates.vue'),
@ -575,7 +583,8 @@ export default {
cursor: not-allowed;
opacity: 0.5;
border-radius: var(--curve-factor);
background: #ffff0080;
background: #ffff0040;
&:hover { background: none; }
}
}
// Error message output

View File

@ -20,7 +20,7 @@ const WidgetMixin = {
overrideUpdateInterval: null,
disableLoader: false, // Prevent ever showing the loader
updater: null, // Stores interval
defaultTimeout: 10000,
defaultTimeout: 50000,
}),
/* When component mounted, fetch initial data */
mounted() {

View File

@ -28,7 +28,7 @@ export const sanitize = (string) => {
export const timestampToDate = (timestamp) => {
const localFormat = navigator.language;
const dateFormat = {
weekday: 'short', day: 'numeric', month: 'short', year: '2-digit',
weekday: 'short', day: 'numeric', month: 'short', year: 'numeric',
};
const date = new Date(timestamp).toLocaleDateString(localFormat, dateFormat);
return `${date}`;
@ -133,16 +133,18 @@ export const getTimeDifference = (startTime, endTime) => {
if (diff < 3600) return `${divide(diff, 60)} minutes`;
if (diff < 86400) return `${divide(diff, 3600)} hours`;
if (diff < 604800) return `${divide(diff, 86400)} days`;
if (diff >= 604800) return `${divide(diff, 604800)} weeks`;
if (diff < 31557600) return `${divide(diff, 604800)} weeks`;
if (diff >= 31557600) return `${divide(diff, 31557600)} years`;
return 'unknown';
};
/* Given a timestamp, return how long ago it was, e.g. '10 minutes' */
export const getTimeAgo = (dateTime) => {
const now = new Date().getTime();
const isHistorical = new Date(dateTime).getTime() < now;
const diffStr = getTimeDifference(dateTime, now);
if (diffStr === 'unknown') return diffStr;
return `${diffStr} ago`;
return isHistorical ? `${diffStr} ago` : `in ${diffStr}`;
};
/* Given the name of a CSS variable, returns it's value */

View File

@ -224,6 +224,7 @@ module.exports = {
cryptoPrices: 'https://api.coingecko.com/api/v3/coins/',
cryptoWatchList: 'https://api.coingecko.com/api/v3/coins/markets/',
cveVulnerabilities: 'https://www.cvedetails.com/json-feed.php',
domainMonitor: 'https://api.whoapi.com',
ethGasPrices: 'https://ethgas.watch/api/gas',
ethGasHistory: 'https://ethgas.watch/api/gas/trend',
exchangeRates: 'https://v6.exchangerate-api.com/v6/',