Skip to content

Commit

Permalink
🔀 Merge pull request #182 from Lissy93/FEATURE/ssl-status-check-options
Browse files Browse the repository at this point in the history
[FEATURE] Allow for status checks to non-HTTPS content
Fixed #181
  • Loading branch information
Lissy93 committed Aug 27, 2021
2 parents 278e648 + ff663f8 commit e3b364f
Show file tree
Hide file tree
Showing 14 changed files with 96 additions and 26 deletions.
8 changes: 6 additions & 2 deletions .env
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Store environmental variables here. All variables are optional.

# PORT=4000 # The port to expose the running application on
# NODE_ENV=production # Can be either development, production or test
# BASE_URL=/ # The default base path for serving up static assets
# PORT=4000 # The port to expose the running application on
# HOST=localhost # The host that Dashy is running on, domain or IP
# BASE_URL=./ # The default base path for serving up static assets
# VUE_APP_DOMAIN # Usually the same as BASE_URL, but accessible in frontend
# IS_DOCKER=true # Usually already set, should be true if running in container
# VUE_APP_VERSION # Again, set automatically using package.json during build
6 changes: 6 additions & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## ⚡️ 1.6.7 - Option for non-SSL status checks plus minor things [PR #182](https://github.com/Lissy93/dashy/pull/182)
- Adds an option for user to use status checks with non-HTTPS services, Re: #181
- Updates the .env template, plus the variables used in the server
- Uses the v-cloak to hide text before it's finished loading
- Fixed the parsing of the update-checker during build

## ⚡️ 1.6.6 - Improved Search & Shortcuts [PR #175](https://github.com/Lissy93/dashy/pull/175)
- Refactors the search algorithm to improve performance and code reusability
- Updates search to ignore case, special characters and minor-typos
Expand Down
1 change: 1 addition & 0 deletions docs/configuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ For more info, see the **[Authentication Docs](/docs/authentication.md)**
**`statusCheck`** | `boolean` | _Optional_ | When set to `true`, Dashy will ping the URL associated with the current service, and display its status as a dot next to the item. The value here will override `appConfig.statusCheck` so you can turn off or on checks for a given service. Defaults to `appConfig.statusCheck`, falls back to `false`
**`statusCheckUrl`** | `string` | _Optional_ | If you've enabled `statusCheck`, and want to use a different URL to what is defined under the item, then specify it here
**`statusCheckHeaders`** | `object` | _Optional_ | If you're endpoint requires any specific headers for the status checking, then define them here
**`statusCheckAllowInsecure`** | `boolean` | By default, any request to insecure content will be blocked. Setting this option to `true` will disable the `rejectUnauthorized` option, enabling you to ping non-HTTPS services. Should only be used when needed, and for URLs that you know are safe. Defaults to `false`
**`color`** | `string` | _Optional_ | An optional color for the text and font-awesome icon to be displayed in. Note that this will override the current theme and so may not display well
**`backgroundColor`** | `string` | _Optional_ | An optional background fill color for the that given item. Again, this will override the current theme and so might not display well against the background
**`provider`** | `string` | _Optional_ | The name of the provider for a given service, useful for when including hosted apps. In some themes, this is visible under the item name
Expand Down
21 changes: 14 additions & 7 deletions docs/status-indicators.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Status Indicators

Dashy has an optional feature that can display a small icon next to each of your running services, indicating it's current status. This is useful if you are using Dashy as your homelab's start page, as it gives you an overview of the health of each of your running services.
Dashy has an optional feature that can display a small icon next to each of your running services, indicating it's current status. This can be useful if you are using Dashy as your homelab's start page, as it gives you an overview of the health of each of your running services. The status feature will show response time, response code, online/ offline check and if applicable, a relevant error message

<p align="center">
<img width="800" src="/docs/assets/status-check-demo.gif" />
Expand All @@ -24,21 +24,21 @@ sections:
description: Firewall Central Management
icon: networking/opnsense.png
url: https://192.168.1.1
statusCheck: false
statusCheck: false
- title: MalTrail
description: Malicious traffic detection system
icon: networking/maltrail.png
url: http://192.168.1.1:8338
statusCheck: true
statusCheck: true
- title: Ntopng
description: Network traffic probe and network use monitor
icon: networking/ntop.png
url: http://192.168.1.1:3001
statusCheck: true
statusCheck: true
```

## Continuous Checking
By default, with status indicators enabled Dashy will check an applications status on page load, and will not keep indicators updated. This is usually desirable behavior. However, if you do want the status indicators to continue to poll your running services, this can be enabled by setting the `statusCheckInterval` attribute. Here you define an interval in seconds, and Dashy will poll your apps every x seconds. Note that if this number is very low (below 5 seconds), you may notice the app running slightly slower.
By default, with status indicators enabled Dashy will check an applications status on page load, and will not keep indicators updated. This is usually desirable behavior. However, if you do want the status indicators to continue to poll your running services, this can be enabled by setting the `statusCheckInterval` attribute. Here you define an interval as an integer in seconds, and Dashy will poll your apps every x seconds. Note that if this number is very low (below 5 seconds), you may notice the app running slightly slower.

The following example, will instruct Dashy to continuously check the status of your services every 20 seconds

Expand All @@ -57,6 +57,9 @@ You can set the `statusCheckUrl` property on any given item in order to do this.
If your service is responding with an error, despite being up and running, it is most likely because custom headers for authentication, authorization or encoding are required. You can define these headers under the `statusCheckHeaders` property for any service. It should be defined as an object format, with the name of header as the key, and header content as the value.
For example, `statusCheckHeaders: { 'X-Custom-Header': 'foobar' }`

## Disabling Security
By default, (if you're using HTTPS) any requests to insecure or non-HTTPS content will be blocked. This will cause the status check to fail. If you trust the endpoint (e.g. you're self-hosting it), then you can disable this security measure for an individual item. This is done by setting `statusCheckAllowInsecure: true`

## Troubleshooting Failing Status Checks
If the status is always returning an error, despite the service being online, then it is most likely an issue with access control, and should be fixed with the correct headers. Hover over the failing status to see the error code and response, in order to know where to start with addressing it.
If your service requires requests to include any authorization in the headers, then use the `statusCheckHeaders` property, as described above.
Expand All @@ -65,12 +68,16 @@ If you are still having issues, it may be because your target application is blo
Access-Control-Allow-Origin: https://location-of-dashy/
Vary: Origin
```
If the URL you are checking is not using HTTPS, then you may need to disable the rejection of insecure requests. This can be done by setting `statusCheckAllowInsecure` to true for a given item.

If you're serving Dashy though a CDN, instead of using the Node server or Docker image, then the Node endpoint that makes requests will not be available to you, and all requests will fail. A workaround for this may be implemented in the future, but in the meantime, your only option is to use the Docker or Node deployment method.

For further troubleshooting, use an application like [Postman](https://postman.com) to diagnose the issue.

## How it Works

When Dashy is loaded, items with `statusCheck` enabled will make a request, to `https://[your-host-name]/ping?url=[address-or-servce]`, which in turn will ping that running service, and respond with a status code. Response time is calculated from the difference between start and end time of the request.
When the app is loaded, if `appConfig.statusCheck: true` is set, or if any items have the `statusCheck: true` enabled, then Dashy will make a request, to `https://[your-host-name]/ping?url=[address-or-servce]` (may al include GET params for headers and the secure flag), which in turn will ping that running service, and respond with a status code. Response time is calculated from the difference between start and end time of the request.

When the response completes, an indicator will display next to each item. The color denotes the status: Yellow while waiting for the response to return, green if request was successful, red if it failed, and grey if it was unable to make the request all together.

All requests are made straight from your server, there is no intermediary. So providing you are hosting Dashy yourself, and are checking the status of other self-hosted services, there shouldn't be any privacy concerns. Requests are made asynchronously, so this won't have any impact on page load speeds. However recurring requests (using `statusCheckInterval`) may run more slowly if the interval between requests is very short.
All requests are made straight from your server, there is no intermediary. So providing you are hosting Dashy yourself, and are checking the status of other self-hosted services, there shouldn't be any privacy concerns. Requests are made asynchronously, so this won't have any significant impact on page load speeds. However recurring requests (using `statusCheckInterval`) may run more slowly if the interval between requests is very short.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Dashy",
"version": "1.6.6",
"version": "1.6.7",
"license": "MIT",
"main": "server",
"scripts": {
Expand Down
32 changes: 26 additions & 6 deletions services/ping.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* endpoint, and then resolve the response status code, time taken, and short message
*/
const axios = require('axios').default;
const https = require('https');

/* Determines if successful from the HTTP response code */
const getResponseType = (code) => {
Expand All @@ -26,9 +27,14 @@ const makeErrorMessage2 = (data) => '❌ Service Error - '
+ `${data.status} - ${data.statusText}`;

/* Kicks of a HTTP request, then formats and renders results */
const makeRequest = (url, render) => {
const makeRequest = (url, headers, insecure, render) => {
const startTime = new Date();
axios.get(url)
const requestMaker = axios.create({
httpsAgent: new https.Agent({
rejectUnauthorized: !insecure,
}),
});
requestMaker.get(url, { headers })
.then((response) => {
const statusCode = response.status;
const { statusText } = response;
Expand All @@ -52,15 +58,29 @@ const makeRequest = (url, render) => {
});
};

const decodeHeaders = (maybeHeaders) => {
if (!maybeHeaders) return {};
const decodedHeaders = decodeURIComponent(maybeHeaders);
let parsedHeaders = {};
try {
parsedHeaders = JSON.parse(decodedHeaders);
} catch (e) { /* Not valid JSON, will just return false */ }
return parsedHeaders;
};

/* Main function, will check if a URL present, and call function */
module.exports = (params, render) => {
if (!params || !params.includes('=')) {
module.exports = (paramStr, render) => {
if (!paramStr || !paramStr.includes('=')) {
render(JSON.stringify({
success: false,
message: '❌ Malformed URL',
}));
} else {
const url = params.split('=')[1];
makeRequest(url, render);
// Prepare the parameters, which are got from the URL
const params = new URLSearchParams(paramStr);
const url = decodeURIComponent(params.get('url'));
const headers = decodeHeaders(params.get('headers'));
const enableInsecure = !!params.get('enableInsecure');
makeRequest(url, headers, enableInsecure, render);
}
};
2 changes: 1 addition & 1 deletion services/print-message.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ module.exports = (ip, port, isDocker) => {
const blanks = (count) => printChars(count, ' ');
if (isDocker) {
// Prepare message for Docker users
const containerId = process.env.HOSTNAME || undefined;
const containerId = process.env.HOST || undefined;
msg = `${chars.BLUE}${stars(91)}${chars.BR}${chars.RESET}`
+ `${chars.CYAN}Welcome to Dashy! 🚀${chars.RESET}${chars.BR}`
+ `${chars.GREEN}Your new dashboard is now up and running `
Expand Down
2 changes: 1 addition & 1 deletion services/update-checker.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const currentVersion = require('../package.json').version;
const packageUrl = 'https://raw.githubusercontent.com/Lissy93/dashy/master/package.json';

const makeMsg = (latestVersion) => {
const parse = (version) => parseInt(version.replaceAll('.', ''), 10);
const parse = (version) => parseInt(version.replace(/\./g, ''), 10);
const difference = parse(latestVersion) - parse(currentVersion);
let msg = '';
if (difference <= 0) {
Expand Down
30 changes: 23 additions & 7 deletions src/components/LinkItems/Item.vue
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export default {
statusCheckHeaders: Object,
statusCheckUrl: String,
statusCheckInterval: Number,
statusCheckAllowInsecure: Boolean,
},
data() {
return {
Expand Down Expand Up @@ -144,18 +145,33 @@ export default {
default: return '"\\f054"';
}
},
/* Checks if a given service is currently online */
checkWebsiteStatus() {
/* Pulls together all user options, returns URL + Get params for ping endpoint */
makeApiUrl() {
const {
url, statusCheckUrl, statusCheckHeaders, statusCheckAllowInsecure,
} = this;
const encode = (str) => encodeURIComponent(str);
this.statusResponse = undefined;
// Find base URL, where the API is hosted
const baseUrl = process.env.VUE_APP_DOMAIN || window.location.origin;
const urlToCheck = this.statusCheckUrl || this.url;
const headers = this.statusCheckHeaders || {};
const endpoint = `${baseUrl}/ping?url=${urlToCheck}`;
axios.get(endpoint, { headers })
// Find correct URL to check, and encode
const urlToCheck = `?&url=${encode(statusCheckUrl || url)}`;
// Get, stringify and encode any headers
const headers = statusCheckHeaders
? `&headers=${encode(JSON.stringify(statusCheckHeaders))}` : '';
// Deterimine if user disabled security
const enableInsecure = statusCheckAllowInsecure ? '&enableInsecure=true' : '';
// Construct the full API endpoint's URL with GET params
return `${baseUrl}/ping/${urlToCheck}${headers}${enableInsecure}`;
},
/* Checks if a given service is currently online */
checkWebsiteStatus() {
const endpoint = this.makeApiUrl();
axios.get(endpoint)
.then((response) => {
if (response.data) this.statusResponse = response.data;
})
.catch(() => {
.catch(() => { // Something went very wrong.
this.statusResponse = {
statusText: 'Failed to make request',
statusSuccess: false,
Expand Down
1 change: 1 addition & 0 deletions src/components/LinkItems/Section.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
:hotkey="item.hotkey"
:enableStatusCheck="shouldEnableStatusCheck(item.statusCheck)"
:statusCheckInterval="getStatusCheckInterval()"
:statusCheckAllowInsecure="item.statusCheckAllowInsecure"
@itemClicked="$emit('itemClicked')"
@triggerModal="triggerModal"
/>
Expand Down
1 change: 1 addition & 0 deletions src/components/LinkItems/StatusIndicator.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export default {
padding: 5px;
transition: all .2s ease-in-out;
cursor: help;
z-index: 5;
&:hover {
transform: scale(1.25);
filter: saturate(2);
Expand Down
7 changes: 6 additions & 1 deletion src/styles/color-themes.scss
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ html[data-theme='material'] {
// --login-form-background-secondary: #f5f5f5cc;
--context-menu-secondary-color: #f5f5f5;
--transparent-white-50: #00000080;
--status-check-tooltip-background: #fff;

--minimal-view-background-color: var(--background);
--minimal-view-title-color: var(--background-darker);
Expand Down Expand Up @@ -596,6 +597,9 @@ html[data-theme='material-dark'] {
--nav-link-text-color-hover: #08B0BB;
--nav-link-background-color-hover: #131a1fc7;
--nav-link-border-color-hover: transparent;

--status-check-tooltip-background: #131a1f;
--status-check-tooltip-color: #e0e0e0;
--curve-factor: 2px;
--curve-factor-navbar: 0;

Expand All @@ -605,7 +609,6 @@ html[data-theme='material-dark'] {
--config-settings-color: #41e2ed;
--scroll-bar-color: #08B0BB;
--scroll-bar-background: #131a1f;
--status-check-tooltip-color: #131a1f;
// --login-form-color: #131a1f;
--login-form-background-secondary: #131a1f;

Expand Down Expand Up @@ -741,6 +744,7 @@ html[data-theme='vaporware'] {
--curve-factor-navbar: 6px;
--login-form-color: #09bfe6;
--config-settings-background: #100e2c;
--status-check-tooltip-background: #100e2c;

.home {
background: linear-gradient(180deg, rgba(16,14,44,1) 10%, rgba(27,24,79,1) 40%, rgba(16,14,44,1) 100%);
Expand Down Expand Up @@ -823,6 +827,7 @@ html[data-theme='cyberpunk'] {
--footer-background: var(--aqua);
--welcome-popup-background: var(--pink);
--welcome-popup-text-color: var(--blue);
--status-check-tooltip-background: var(--blue);
--font-headings: 'Audiowide', cursive;
}

Expand Down
4 changes: 4 additions & 0 deletions src/styles/global-styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ html {
}
}

/* Hide text, and show 'Loading...' while Vue is intializing tags */
[v-cloak] > * { display:none }
[v-cloak]::before { content: "loading…" }

/* Overriding styles for the modal component */
.vm--modal, .dashy-modal {
box-shadow: 0 40px 70px -2px hsl(0deg 0% 0% / 60%), 1px 1px 6px var(--primary) !important;
Expand Down
5 changes: 5 additions & 0 deletions src/utils/ConfigSchema.json
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,11 @@
"statusCheckHeaders": {
"type": "object",
"description": " If you're endpoint requires any specific headers for the status checking, then define them here"
},
"statusCheckAllowInsecure": {
"type": "boolean",
"default": false,
"description": "Allows for running status checks on insecure content/ non-HTTPS apps"
}
}
}
Expand Down

0 comments on commit e3b364f

Please sign in to comment.