diff --git a/docs/privacy.md b/docs/privacy.md
index df0d4e6d6d..4d1b82ab14 100644
--- a/docs/privacy.md
+++ b/docs/privacy.md
@@ -204,7 +204,7 @@ Dashy supports [Widgets](/docs/widgets.md) for displaying dynamic content. Below
| [Astronomy Picture of the Day](/docs/widgets.md#astronomy-picture-of-the-day) | `https://apod.as93.net` | [NASA's Privacy Policy](https://www.nasa.gov/about/highlights/HP_Privacy.html) |
| [GitHub Trending](/docs/widgets.md#github-trending) | `https://trend.doforce.xyz` | No Policy Available |
| [GitHub Profile Stats](/docs/widgets.md#github-profile-stats) | `https://github-readme-stats.vercel.app` | [GitHub's Privacy Policy](https://docs.github.com/en/github/site-policy/github-privacy-statement) |
-| [Cron Monitoring (Health Checks)](/docs/widgets.md#cron-monitoring-health-checks) | `https://healthchecks.io` | [Health-Checks Privacy Policy](https://healthchecks.io/privacy/) |
+| [Healthchecks Status](/docs/widgets.md#healthchecks-status) | `https://healthchecks.io` | [Health-Checks Privacy Policy](https://healthchecks.io/privacy/) |
| [Hacker News Trending](/docs/widgets.md#hacker-news-trending) | `https://hacker-news.firebaseio.com` | [Y Combinator Privacy Policy](https://www.ycombinator.com/legal#privacy) |
| [Minecraft Server Status](/docs/widgets.md#minecraft-server-status) | `https://api.mcsrvstat.us` | No Policy Available |
| [MVG](/docs/widgets.md#mvg) | `https://www.mvg.de/api/fib/v2/` | No Policy Available |
diff --git a/docs/widgets.md b/docs/widgets.md
index 65cabbd1e5..6f49c40539 100644
--- a/docs/widgets.md
+++ b/docs/widgets.md
@@ -44,7 +44,6 @@ Dashy has support for displaying dynamic content in the form of widgets. There a
- [Minecraft Server](#minecraft-server)
- **[Self-Hosted Services Widgets](#self-hosted-services-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)
@@ -1227,19 +1226,21 @@ Display stats from your GitHub profile, using embedded cards from [anuraghazra/g
### HealthChecks Status
-Display status of one or more HealthChecks project(s). Works with healthchecks.io and your selfhosted instance.
+Display cron job monitor status of one or more HealthChecks project(s). Works with healthchecks.io and your selfhosted instance.
-

+
#### Options
**Field** | **Type** | **Required** | **Description**
--- | --- | --- | ---
**`host`** | `string` | Optional | The base url of your instance, default is `https://healthchecks.io`
-**`apiKey`** | `string` or `array` | Required | One or more API keys for your healthcheck projects. (Read-only works fine)
+**`apiKey`** | `string` (or `string[]` for multiple) | Required | API key(s) for project(s) to monitor. Generate a separate key per project (read-only is fine), under Project --> Settings --> API Access
+
+##### Example
```yaml
-- type: HealthChecks
+- type: health-checks
options:
host: https://healthcheck.your-domain.de
apiKey:
@@ -1247,9 +1248,17 @@ Display status of one or more HealthChecks project(s). Works with healthchecks.i
- zxywvu...
```
+Or, a single project:
+```yaml
+ - type: health-checks
+ options:
+ host: https://healthchecks.io
+ apiKey: hcw_xxxxxxxxxxxxxxxxxxxxxxxx
+```
+
#### Info
-- **CORS**: 🟢 Enabled
+- **CORS**: 🟠 Proxied
- **Auth**: 🟢 Required
- **Price**: 🟢 Free / Paid / Self-hosted
- **Host**: Managed Instance or Self-Hosted (see [healthchecks/healthchecks](https://github.com/healthchecks/healthchecks))
@@ -1661,37 +1670,6 @@ Note that this widget is not available if you are running Dashy in a container o
---
-### Cron Monitoring (Health Checks)
-
-Cron job monitoring using [Health Checks](https://github.com/healthchecks/healthchecks). Both managed and self-hosted instances are supported.
-
-
-
-#### 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
-```
-
-#### Info
-
-- **CORS**: 🟢 Enabled
-- **Auth**: 🔴 Required
-- **Price**: 🟠 Free plan (up to 20 services, or self-host for unlimited)
-- **Host**: Managed Instance or Self-Hosted (see [GitHub - HealthChecks](https://github.com/healthchecks/healthchecks))
-- **Privacy**: _See [Health-Checks Privacy Policy](https://healthchecks.io/privacy/)_
-
----
-
### CPU History (NetData)
Pull recent CPU usage history from NetData.
diff --git a/src/components/Widgets/HealthChecks.vue b/src/components/Widgets/HealthChecks.vue
index 2201b11a5e..31e6133274 100644
--- a/src/components/Widgets/HealthChecks.vue
+++ b/src/components/Widgets/HealthChecks.vue
@@ -5,7 +5,7 @@
:key="cron.id"
>
-
{{ formatStatus(cron.status) }}
+
{{ formatStatus(cron.status) }}
import WidgetMixin from '@/mixins/WidgetMixin';
-import { widgetApiEndpoints } from '@/utils/config/defaults';
import { capitalize, timestampToDateTime } from '@/utils/MiscHelpers';
export default {
@@ -32,49 +31,60 @@ export default {
};
},
computed: {
+ baseUrl() {
+ return this.options.host || 'https://healthchecks.io';
+ },
/* API endpoint, either for self-hosted or managed instance */
endpoint() {
- if (this.options.host) return `${this.options.host}/api/v1/checks`;
- return `${widgetApiEndpoints.healthChecks}`;
+ return `${this.baseUrl}/api/v1/checks/`;
},
- apiKey() {
- if (!this.options.apiKey) {
- this.error('An API key is required, please see the docs for more info');
- }
- if (typeof this.options.apiKey === 'string') {
- return [this.parseAsEnvVar(this.options.apiKey)];
- }
- return this.options.apiKey;
+ /* User's API key(s), normalised to an array, or null if unset */
+ apiKeys() {
+ const { apiKey } = this.options;
+ if (!apiKey) return null;
+ const keys = Array.isArray(apiKey) ? apiKey : [apiKey];
+ return keys.map((key) => this.parseAsEnvVar(key));
},
},
methods: {
+ /* Make status summary (emoji + name) */
formatStatus(status) {
- let symbol = '';
- if (status === 'up') symbol = '✔';
- if (status === 'down') symbol = '✘';
- if (status === 'new') symbol = '❖';
- if (status === 'paused') symbol = '⏸';
- if (status === 'running') symbol = '▶';
- return `${symbol} ${capitalize(status)}`;
+ const symbols = {
+ up: '✔', down: '✘', new: '❖', paused: '⏸', grace: '⚠', started: '▶',
+ };
+ return `${symbols[status] || '❔'} ${capitalize(status || 'unknown')}`;
},
formatDate(timestamp) {
return timestampToDateTime(timestamp);
},
- /* Make GET request to CoinGecko API endpoint */
fetchData() {
+ if (!this.apiKeys) {
+ this.error('An API key is required, please see the docs for more info');
+ this.finishLoading();
+ return;
+ }
this.overrideProxyChoice = true;
- const results = [];
- this.apiKey.forEach((key) => {
- const authHeaders = { 'X-Api-Key': key };
- this.makeRequest(this.endpoint, authHeaders).then(
- (response) => { this.processData(response, results); },
- );
+ const requests = this.apiKeys.map(
+ (key) => this.makeRequest(this.endpoint, { 'X-Api-Key': key }),
+ );
+ Promise.allSettled(requests).then((outcomes) => {
+ const results = [];
+ outcomes.forEach((outcome) => {
+ if (outcome.status === 'fulfilled') this.processData(outcome.value, results);
+ });
+ results.sort((a, b) => (a.name > b.name ? 1 : -1));
+ this.crons = results;
});
- results.sort((a, b) => ((a.name > b.name) ? 1 : -1));
- this.crons = results;
},
- /* Assign data variables to the returned data */
+ /* Map the API response into the cron list, guarding against bad responses */
processData(data, results) {
+ if (!data || !Array.isArray(data.checks)) {
+ this.error(
+ 'Unexpected response, please check your host URL and API key are correct.'
+ + ' useProxy may be required if your Healchecks API is blocking Dashy with CORS'
+ );
+ return;
+ }
data.checks.forEach((cron) => {
results.push({
id: cron.slug,
@@ -87,11 +97,9 @@ export default {
url: this.makeUrl(cron.unique_key),
});
});
- return results;
},
makeUrl(cronId) {
- const base = this.options.host || 'https://healthchecks.io';
- return `${base}/checks/${cronId}/details`;
+ return `${this.baseUrl}/checks/${cronId}/details`;
},
pingTimeTooltip(cron) {
const { lastPing, nextPing, pingCount } = cron;
@@ -123,7 +131,9 @@ export default {
&.up { color: var(--success); }
&.down { color: var(--danger); }
&.new { color: var(--widget-text-color); }
- &.running { color: var(--warning); }
+ &.grace { color: var(--warning); }
+ &.unknown { color: var(--warning); }
+ &.started { color: var(--info); }
&.paused { color: var(--info); }
}
}