mirror of https://github.com/Lissy93/dashy.git
✨ Adds a cron job monitoring widget
This commit is contained in:
parent
676d1cb106
commit
8ae310ebd3
|
@ -25,6 +25,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a
|
|||
- [Public IP Address](#public-ip)
|
||||
- [Self-Hosted Services Widgets](#dynamic-widgets)
|
||||
- [System Info](#system-info)
|
||||
- [Cron Monitoring](#cron-monitoring-health-checks)
|
||||
- [CPU History](#cpu-history-netdata)
|
||||
- [Memory History](#memory-history-netdata)
|
||||
- [System Load History](#load-history-netdata)
|
||||
|
@ -584,6 +585,29 @@ _No config options._
|
|||
|
||||
---
|
||||
|
||||
### Cron Monitoring (Health Checks)
|
||||
|
||||
Cron job monitoring using [Health Checks](https://github.com/healthchecks/healthchecks). Both managed and self-hosted instances are supported.
|
||||
|
||||
<p align="center"><img width="400" src="https://i.ibb.co/Ptf2kwm/health-checks.png" /></p>
|
||||
|
||||
##### Options
|
||||
|
||||
**Field** | **Type** | **Required** | **Description**
|
||||
--- | --- | --- | ---
|
||||
**`apiKey`** | `string` | Required | A read-only API key for the project to monitor. You can generate this by selecting a Project --> Settings --> API Access. Note that you must generate a separate key for each project
|
||||
**`host`** | `string` | _Optional_ | If you're self-hosting, or using any instance other than the official (healthchecks.io), you will need to specify the host address. E.g. `https://healthchecks.example.com` or `http://cron-monitoing.local`
|
||||
|
||||
##### Example
|
||||
|
||||
```yaml
|
||||
- type: health-checks
|
||||
options:
|
||||
apiKey: XXXXXXXXX
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### CPU History (NetData)
|
||||
|
||||
Pull recent CPU usage history from NetData.
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
<template>
|
||||
<div class="health-checks-wrapper" v-if="crons">
|
||||
<div
|
||||
class="cron-row"
|
||||
v-for="cron in crons" :key="cron.id"
|
||||
v-tooltip="pingTimeTooltip(cron)"
|
||||
>
|
||||
<div class="status">
|
||||
<p :class="cron.status">{{ cron.status | formatStatus }}</p>
|
||||
</div>
|
||||
<div class="info">
|
||||
<p class="cron-name">{{ cron.name }}</p>
|
||||
<p class="cron-desc">{{ cron.desc }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios';
|
||||
import WidgetMixin from '@/mixins/WidgetMixin';
|
||||
import { widgetApiEndpoints, serviceEndpoints } from '@/utils/defaults';
|
||||
import { capitalize, timestampToDateTime } from '@/utils/MiscHelpers';
|
||||
|
||||
export default {
|
||||
mixins: [WidgetMixin],
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
crons: null,
|
||||
};
|
||||
},
|
||||
filters: {
|
||||
formatStatus(status) {
|
||||
let symbol = '';
|
||||
if (status === 'up') symbol = '✔';
|
||||
if (status === 'down') symbol = '✘';
|
||||
if (status === 'new') symbol = '❖';
|
||||
return `${symbol} ${capitalize(status)}`;
|
||||
},
|
||||
formatDate(timestamp) {
|
||||
return timestampToDateTime(timestamp);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
/* API endpoint, either for self-hosted or managed instance */
|
||||
endpoint() {
|
||||
if (this.options.host) return `${this.options.host}/api/v1/checks`;
|
||||
return `${widgetApiEndpoints.healthChecks}`;
|
||||
},
|
||||
proxyReqEndpoint() {
|
||||
const baseUrl = process.env.VUE_APP_DOMAIN || window.location.origin;
|
||||
return `${baseUrl}${serviceEndpoints.corsProxy}`;
|
||||
},
|
||||
apiKey() {
|
||||
if (!this.options.apiKey) {
|
||||
this.error('An API key is required, please see the docs for more info');
|
||||
}
|
||||
return this.options.apiKey;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/* Make GET request to CoinGecko API endpoint */
|
||||
fetchData() {
|
||||
const requestConfig = {
|
||||
method: 'GET',
|
||||
url: this.proxyReqEndpoint,
|
||||
headers: {
|
||||
'access-control-request-headers': '*',
|
||||
'Target-URL': this.endpoint,
|
||||
CustomHeaders: JSON.stringify({ 'X-Api-Key': this.apiKey }),
|
||||
},
|
||||
};
|
||||
axios.request(requestConfig)
|
||||
.then((response) => {
|
||||
this.processData(response.data);
|
||||
}).catch((error) => {
|
||||
this.error('Unable to fetch cron data', error);
|
||||
}).finally(() => {
|
||||
this.finishLoading();
|
||||
});
|
||||
},
|
||||
/* Assign data variables to the returned data */
|
||||
processData(data) {
|
||||
const results = [];
|
||||
data.checks.forEach((cron) => {
|
||||
results.push({
|
||||
id: cron.slug,
|
||||
name: cron.name,
|
||||
desc: cron.desc,
|
||||
status: cron.status,
|
||||
pingCount: cron.n_pings,
|
||||
lastPing: cron.last_ping,
|
||||
nextPing: cron.next_ping,
|
||||
url: this.makeUrl(cron.unique_key),
|
||||
});
|
||||
});
|
||||
this.crons = results;
|
||||
},
|
||||
makeUrl(cronId) {
|
||||
const base = this.options.host || 'https://healthchecks.io';
|
||||
return `${base}/checks/${cronId}/details`;
|
||||
},
|
||||
pingTimeTooltip(cron) {
|
||||
const { lastPing, nextPing, pingCount } = cron;
|
||||
const content = `<b>Total number of Pings:</b> ${pingCount}<br>`
|
||||
+ `<b>Last Ping:</b> ${timestampToDateTime(lastPing)}<br>`
|
||||
+ `<b>Next Ping:</b>${timestampToDateTime(nextPing)}`;
|
||||
return {
|
||||
content, html: true, trigger: 'hover focus', delay: 250, classes: 'ping-times-tt',
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.health-checks-wrapper {
|
||||
color: var(--widget-text-color);
|
||||
.cron-row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0.25rem 0;
|
||||
.status {
|
||||
min-width: 5rem;
|
||||
font-size: 1.2rem;
|
||||
font-weight: bold;
|
||||
p {
|
||||
margin: 0;
|
||||
color: var(--info);
|
||||
&.up { color: var(--success); }
|
||||
&.down { color: var(--danger); }
|
||||
&.new { color: var(--neutral); }
|
||||
}
|
||||
}
|
||||
.info {
|
||||
p.cron-name {
|
||||
margin: 0.25rem 0;
|
||||
font-weight: bold;
|
||||
color: var(--widget-text-color);
|
||||
}
|
||||
p.cron-desc {
|
||||
margin: 0;
|
||||
color: var(--widget-text-color);
|
||||
opacity: var(--dimming-factor);
|
||||
}
|
||||
}
|
||||
&:not(:last-child) {
|
||||
border-bottom: 1px dashed var(--widget-text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.ping-times-tt {
|
||||
min-width: 20rem;
|
||||
}
|
||||
</style>
|
|
@ -88,6 +88,13 @@
|
|||
@error="handleError"
|
||||
:ref="widgetRef"
|
||||
/>
|
||||
<HealthChecks
|
||||
v-else-if="widgetType === 'health-checks'"
|
||||
:options="widgetOptions"
|
||||
@loading="setLoaderState"
|
||||
@error="handleError"
|
||||
:ref="widgetRef"
|
||||
/>
|
||||
<IframeWidget
|
||||
v-else-if="widgetType === 'iframe'"
|
||||
:options="widgetOptions"
|
||||
|
@ -239,6 +246,7 @@ import ExchangeRates from '@/components/Widgets/ExchangeRates.vue';
|
|||
import Flights from '@/components/Widgets/Flights.vue';
|
||||
import GitHubTrending from '@/components/Widgets/GitHubTrending.vue';
|
||||
import GitHubProfile from '@/components/Widgets/GitHubProfile.vue';
|
||||
import HealthChecks from '@/components/Widgets/HealthChecks.vue';
|
||||
import IframeWidget from '@/components/Widgets/IframeWidget.vue';
|
||||
import Jokes from '@/components/Widgets/Jokes.vue';
|
||||
import NdCpuHistory from '@/components/Widgets/NdCpuHistory.vue';
|
||||
|
@ -277,6 +285,7 @@ export default {
|
|||
Flights,
|
||||
GitHubTrending,
|
||||
GitHubProfile,
|
||||
HealthChecks,
|
||||
IframeWidget,
|
||||
Jokes,
|
||||
NdCpuHistory,
|
||||
|
|
|
@ -46,6 +46,7 @@ module.exports = {
|
|||
save: '/config-manager/save',
|
||||
rebuild: '/config-manager/rebuild',
|
||||
systemInfo: '/system-info',
|
||||
corsProxy: '/cors-proxy',
|
||||
},
|
||||
/* List of built-in themes, to be displayed within the theme-switcher dropdown */
|
||||
builtInThemes: [
|
||||
|
@ -213,7 +214,7 @@ module.exports = {
|
|||
exchangeRates: 'https://v6.exchangerate-api.com/v6/',
|
||||
flights: 'https://aerodatabox.p.rapidapi.com/flights/airports/icao/',
|
||||
githubTrending: 'https://gh-trending-repos.herokuapp.com/',
|
||||
healthChecks: 'https://healthchecks.io/api/v1/',
|
||||
healthChecks: 'https://healthchecks.io/api/v1/checks',
|
||||
holidays: 'https://kayaposoft.com/enrico/json/v2.0/?action=getHolidaysForDateRange',
|
||||
jokes: 'https://v2.jokeapi.dev/joke/',
|
||||
news: 'https://api.currentsapi.services/v1/latest-news',
|
||||
|
|
Loading…
Reference in New Issue