diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 34032168..0deae4ab 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## ⚡️ 2.0.0 - Small Fixes and Docker Multi-Arch Build [PR #451](https://github.com/Lissy93/dashy/pull/451) +- Fixes full-height sections for mobile and Safari (Re: #432, #442) +- Fixes empty section visible in search (Re: #447) +- Fixes numbers omited from tag names (Re: #430) +- Option for custom status code in status check (Re: #456, #448) +- Adds @stuu3k's dashboard to showcase (Re: #446) +- Switches recover and death count in Covid widget (Re: #148) +- Improved contrast in light material theme +- Adds new script to lint, test, build and publish a multi-architecture Docker image to various registries + ## 💄 1.9.9 - Minor UI + Docs Updates [PR #431](https://github.com/Lissy93/dashy/pull/431) - Improved theme support for widgets - Better widget layout in Workspace and Minimal views diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 38486ec0..677f7360 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -59,5 +59,6 @@ body: ## Thanks 🙏 Sorry you are having issues with Dashy, and thank you for raising this ticket - in doing so you are helping to make the app better for everyone 💪 You should expect a reply within the next 48 hours :) + ⭐️ If you are not a bot, please star the repo before submitting your ticket. validations: - required: false \ No newline at end of file + required: false diff --git a/.github/workflows/manage-pending-labels.yml b/.github/workflows/manage-pending-labels.yml index 13f968f9..46d48a32 100644 --- a/.github/workflows/manage-pending-labels.yml +++ b/.github/workflows/manage-pending-labels.yml @@ -19,7 +19,7 @@ jobs: add-awaiting-author: runs-on: ubuntu-latest - if: ${{!github.event.issue.pull_request && github.event.comment.author_association != 'COLLABORATOR' && github.event.comment.author_association != 'OWNER' && github.event.issue.state === 'open' }} + if: ${{!github.event.issue.pull_request && github.event.comment.author_association != 'COLLABORATOR' && github.event.comment.author_association != 'OWNER' && github.event.issue.state == 'open' }} steps: - name: Add Awaiting Author labels when Updated uses: actions-cool/issues-helper@v2 diff --git a/README.md b/README.md index 539bde4a..7c2b7dfb 100644 --- a/README.md +++ b/README.md @@ -312,6 +312,7 @@ There are several different ways you can launch apps. You can specify the defaul - `newtab` - The app will be launched in a new tab (or use Ctrl + Click) - `modal` - Launch app in a resizable/ movable popup modal on the current page (or use Alt + Click) - `workspace` - Changes to Workspace view and launches app +- `clipboard` - Copy the app's URL to your system clipboard - `top` - Opens in the top-most browsing context, useful if you're accessing Dashy through an iframe --- @@ -462,7 +463,7 @@ Several areas that we need a bit of help with at the moment are: - Complete a [short survey](https://survey.typeform.com/to/gl0L68ou) to have your say about future features - Share your dashboard in the [Showcase](https://github.com/Lissy93/dashy/blob/master/docs/showcase.md#dashy-showcase-), to inspire others - Spread the word by sharing Dashy or a screenshot of your dashboard to help new users discover it -- Submit a PR to add a new feature, fix a bug, update the docs, add a theme, or something else +- Submit a PR to add a new feature, fix a bug, update the docs, add a theme, widget or something else - Star Dashy on GitHub/ DockerHub or leave an upvote / review on [these platforms](https://github.com/Lissy93/dashy/blob/master/docs/contributing.md#star-upvote-or-leave-a-review) [![Sponsor Lissy93 on GitHub](./docs/assets/sponsor-button.svg)](https://github.com/sponsors/Lissy93) @@ -540,8 +541,9 @@ To set up the development environment: 1. Get Code: `git clone https://github.com/Lissy93/dashy.git` and `cd dashy` 2. Install dependencies: `yarn` 3. Start dev server: `yarn dev` +4. Open the browser: `http://localhost:8080` -Hot reload is enabled, so changes will be automatically detected, compiled and, refreshed. +When you're ready, you can build the production app with `yarn build`, and then run it with `yarn start` If you're new to web development, I've put together a short [list of resources](https://github.com/Lissy93/dashy/blob/master/docs/developing.md#resources-for-beginners) to help beginners get started @@ -556,22 +558,6 @@ If you're new to web development, I've put together a short [list of resources]( --- -## Release Schedule 🗞️ - -> For full release, automation and CI documentation, see: [**Releases & Workflows**](./docs/release-workflow.md) - -Dashy is under active development, with features, improvements, and changes pushed almost daily. - -We're using [Semantic Versioning](https://semver.org/) to indicate major, minor, and patch versions. You can find the current version number in the readme and check the version of your app under the config menu. The version number is pulled from the [package.json](https://github.com/Lissy93/dashy/blob/master/package.json#L3) file. - -Typically there is a new major release every 2 - 4 weeks, usually on Sunday, and you can view these under the [Releases Page](https://github.com/Lissy93/dashy/releases) and [on DockerHub](https://hub.docker.com/r/lissy93/dashy/tags). In addition, new minor versions are pushed several times a week and are [tagged here](https://github.com/Lissy93/dashy/tags). - -For a full breakdown of each change, you can view the [Changelog](https://github.com/Lissy93/dashy/blob/master/.github/CHANGELOG.md). In addition, each new feature or significant change needs to be submitted through [a pull request](https://github.com/Lissy93/dashy/pulls?q=is%3Apr), which makes it easy to review and track these changes, and roll back if needed. - -**[⬆️ Back to Top](#dashy)** - ---- - ## Documentation 📘 > For full docs, see: **[Documentation Contents](./docs/readme.md)** #### Running Dashy @@ -581,6 +567,17 @@ For a full breakdown of each change, you can view the [Changelog](https://github - 💻 [Management](/docs/management.md) - Managing your app, updating, security, web server configuration, etc - 🚒 [Troubleshooting](/docs/troubleshooting.md) - Common errors and problems, and how to fix them +#### Feature Docs +- 🛡️ [Authentication](/docs/authentication.md) - Guide to setting up authentication to protect your dashboard +- 🌈 [Alternate Views](/docs/alternate-views.md) - Outline of available pages / views and item opening methods +- 💾 [Backup & Restore](/docs/backup-restore.md) - Guide to backing up config with Dashy's cloud sync feature +- 🧸 [Icons](/docs/icons.md) - Outline of all available icon types for sections and items, with examples +- 🌐 [Multi-Language Support](/docs/multi-language-support.md) - Switching languages, and adding a new locales +- 🚦 [Status Indicators](/docs/status-indicators.md) - Using Dashy to monitor uptime and status of your apps +- 🔍 [Searching & Shortcuts](/docs/searching.md) - Searching, launching methods + keyboard shortcuts +- 🎨 [Theming](/docs/theming.md) - Complete guide to applying, writing and modifying themes + styles +- 📊 [Widgets](/docs/widgets.md) - List of all dynamic content widgets, with usage guides and examples + #### Development and Contributing - 🧱 [Developing](/docs/developing.md) - Running Dashy development server locally, and general workflow - 🛎️ [Development Guides](/docs/development-guides.md) - Common development tasks, to help new contributors @@ -589,16 +586,6 @@ For a full breakdown of each change, you can view the [Changelog](https://github - 🏆 [Credits](/docs/credits.md) - Shout out to the amazing people who have contributed so far - 🗞️ [Release Workflow](/docs/release-workflow.md) - Info about releases, CI and automated tasks -#### Feature Docs -- 🛡️ [Authentication](/docs/authentication.md) - Guide to setting up authentication to protect your dashboard -- 🧿 [Alternate Views](/docs/alternate-views.md) - Outline of available pages / views and item opening methods -- 💾 [Backup & Restore](/docs/backup-restore.md) - Guide to Dashy's cloud sync feature -- 🧸 [Icons](/docs/icons.md) - Outline of all available icon types for sections and items -- 🌐 [Language Switching](/docs/multi-language-support.md) - How to change language, add a language, or update text -- 🚦 [Status Indicators](/docs/status-indicators.md) - Using Dashy to monitor uptime and status of your apps -- 🔍 [Searching & Shortcuts](/docs/searching.md) - Finding and launching your apps, and using keyboard shortcuts -- 🎨 [Theming](/docs/theming.md) - Complete guide to applying, writing and modifying themes and styles - #### Misc - 🔐 [Privacy & Security](/docs/privacy.md) - List of requests, potential issues, and security resources - 📄 [License](/LICENSE) - Copy of the MIT License @@ -612,14 +599,11 @@ For a full breakdown of each change, you can view the [Changelog](https://github ## Roadmap 🛣️ -> For past and future app updates, see: [**Changelog**](/.github/CHANGELOG.md) +For upcoming features that will be released in the near future, see the [**Current Roadmap**](https://github.com/Lissy93/dashy/discussions/405) -The following features and tasks are planned for the near future. -- Widget support- cards showing live stats and interactive content from your self-hosted services -- ✅ UI editor and visual configurator -- Replacement of Node backend with Go - - **[⬆️ Back to Top](#dashy)** +For past updates, see the [**Changelog**](/.github/CHANGELOG.md) + +**[⬆️ Back to Top](#dashy)** --- diff --git a/docs/configuring.md b/docs/configuring.md index 5f5517a0..64b5028b 100644 --- a/docs/configuring.md +++ b/docs/configuring.md @@ -193,6 +193,7 @@ For more info, see the **[Authentication Docs](/docs/authentication.md)** **`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` | _Optional_ | 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 for the current item. Defaults to `false` +**`statusCheckAcceptCodes`** | `string` | _Optional_ | If your service's response code is anything other than 2xx, then you can opt to specify an alternative success code. E.g. if you expect your server to return 403, but still want the status indicator to be green, set this value to `403` **`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 diff --git a/docs/status-indicators.md b/docs/status-indicators.md index 0c473e8e..b602f571 100644 --- a/docs/status-indicators.md +++ b/docs/status-indicators.md @@ -60,18 +60,29 @@ 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` +## Allowing Alternative Status Codes +If you expect your service to return a status code that is not in the 2XX range, and still want the indicator to be green, then you can specify an expected status code under `statusCheckAcceptCodes` for a given item. For example, `statusCheckAcceptCodes: '403,418'` + ## 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. +If you're using status checks, and despite a given service being online, the check is displaying an error, there are a couple of things you can look at: + +If your service requires requests to include any authorization in the headers, then use the `statusCheckHeaders` property, as described [above](#setting-custom-headers). + If you are still having issues, it may be because your target application is blocking requests from Dashy's IP. This is a [CORS error](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS), and can be fixed by setting the headers on your target app, to include: ``` -Access-Control-Allow-Origin: https://[dashy-location]/ +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 the URL you are checking has an unsigned certificate, or 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 your service is online, but responds with a status code that is not in the 2xx range, then you can use `statusCheckAcceptCodes` to set an accepted status code. If you get an error, like `Service Unavailable: Server resulted in a fatal error`, even when it's definitely online, this is most likely caused by missing the protocol. Don't forget to include `https://` (or whatever protocol) before the URL, and ensure that if needed, you've specified the port. +Running Dashy in HOST network mode, instead of BRIDGE will allow status check access to other services in HOST mode. For more info, see [#445](https://github.com/Lissy93/dashy/discussions/445). + +If you have firewall rules configured, then ensure that they don't prevent Dashy from making requests to the other services you are trying to access. + Currently, the status check needs a page to be rendered, so if this URL in your browser does not return anything, then status checks will not work. This may be modified in the future, but in the meantime, a fix would be to make your own status service, which just checks if your app responds with whatever code you'd like, and then return a 200 plus renders an arbitrary message. Then just point `statusCheckUrl` to your custom page. For further troubleshooting, use an application like [Postman](https://postman.com) to diagnose the issue. Set the parameter to `GET`, and then make a call to: `https://[url-of-dashy]/status-check/?&url=[service-url]`. Where the service URL must have first been encoded (e.g. with `encodeURIComponent()` or [urlencoder.io](https://www.urlencoder.io/)) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 721a78ec..6b07cb62 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -209,8 +209,14 @@ Vary: Origin ``` If the URL you are checking has an unsigned certificate, or 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 your service is online, but responds with a status code that is not in the 2xx range, then you can use `statusCheckAcceptCodes` to set an accepted status code. + If you get an error, like `Service Unavailable: Server resulted in a fatal error`, even when it's definitely online, this is most likely caused by missing the protocol. Don't forget to include `https://` (or whatever protocol) before the URL, and ensure that if needed, you've specified the port. +Running Dashy in HOST network mode, instead of BRIDGE will allow status check access to other services in HOST mode. For more info, see [#445](https://github.com/Lissy93/dashy/discussions/445). + +If you have firewall rules configured, then ensure that they don't prevent Dashy from making requests to the other services you are trying to access. + Currently, the status check needs a page to be rendered, so if this URL in your browser does not return anything, then status checks will not work. This may be modified in the future, but in the meantime, a fix would be to make your own status service, which just checks if your app responds with whatever code you'd like, and then return a 200 plus renders an arbitrary message. Then just point `statusCheckUrl` to your custom page. For further troubleshooting, use an application like [Postman](https://postman.com) to diagnose the issue. Set the parameter to `GET`, and then make a call to: `https://[url-of-dashy]/status-check/?&url=[service-url]`. Where the service URL must have first been encoded (e.g. with `encodeURIComponent()` or [urlencoder.io](https://www.urlencoder.io/)) diff --git a/package.json b/package.json index 83331ee7..581dcfca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Dashy", - "version": "1.9.9", + "version": "2.0.0", "license": "MIT", "main": "server", "author": "Alicia Sykes (https://aliciasykes.com)", @@ -96,4 +96,4 @@ "> 1%", "last 2 versions" ] -} \ No newline at end of file +} diff --git a/services/status-check.js b/services/status-check.js index cebd2645..6f52482c 100644 --- a/services/status-check.js +++ b/services/status-check.js @@ -7,7 +7,8 @@ const axios = require('axios').default; const https = require('https'); /* Determines if successful from the HTTP response code */ -const getResponseType = (code) => { +const getResponseType = (code, validCodes) => { + if (validCodes && String(validCodes).includes(String(code))) return true; if (Number.isNaN(code)) return false; const numericCode = parseInt(code, 10); return (numericCode >= 200 && numericCode <= 302); @@ -27,7 +28,8 @@ const makeErrorMessage2 = (data) => '❌ Service Error - ' + `${data.status} - ${data.statusText}`; /* Kicks of a HTTP request, then formats and renders results */ -const makeRequest = (url, headers, insecure, render) => { +const makeRequest = (url, headers, insecure, acceptCodes, render) => { + const validCodes = acceptCodes && acceptCodes !== 'null' ? acceptCodes : null; const startTime = new Date(); const requestMaker = axios.create({ httpsAgent: new https.Agent({ @@ -38,22 +40,35 @@ const makeRequest = (url, headers, insecure, render) => { .then((response) => { const statusCode = response.status; const { statusText } = response; - const successStatus = getResponseType(statusCode); + const successStatus = getResponseType(statusCode, validCodes); const serverName = response.request.socket.servername; const timeTaken = (new Date() - startTime); const results = { statusCode, statusText, serverName, successStatus, timeTaken, }; - const messageText = makeMessageText(results); - results.message = messageText; + results.message = makeMessageText(results); return results; }) .catch((error) => { - render(JSON.stringify({ - successStatus: false, - message: error.response ? makeErrorMessage2(error.response) : makeErrorMessage(error), - })); + const response = error ? (error.response || {}) : {}; + const returnCode = response.status || response.code; + if (validCodes && String(validCodes).includes(returnCode)) { // Success overridden by user + const results = { + successStatus: getResponseType(returnCode, validCodes), + statusCode: returnCode, + statusText: response.statusText, + timeTaken: (new Date() - startTime), + }; + results.message = makeMessageText(results); + return results; + } else { // Request failed + return { + successStatus: false, + message: error.response ? makeErrorMessage2(error.response) : makeErrorMessage(error), + }; + } }).then((results) => { + // Request completed (either successfully, or failed) - render results render(JSON.stringify(results)); }); }; @@ -68,19 +83,26 @@ const decodeHeaders = (maybeHeaders) => { return parsedHeaders; }; +/* Returned if the URL param is not present or correct */ +const immediateError = (render) => { + render(JSON.stringify({ + successStatus: false, + message: '❌ Missing or Malformed URL', + })); +}; + /* Main function, will check if a URL present, and call function */ module.exports = (paramStr, render) => { if (!paramStr || !paramStr.includes('=')) { - render(JSON.stringify({ - success: false, - message: '❌ Malformed URL', - })); + immediateError(render); } else { // Prepare the parameters, which are got from the URL const params = new URLSearchParams(paramStr); const url = decodeURIComponent(params.get('url')); + const acceptCodes = decodeURIComponent(params.get('acceptCodes')); const headers = decodeHeaders(params.get('headers')); const enableInsecure = !!params.get('enableInsecure'); - makeRequest(url, headers, enableInsecure, render); + if (!url || url === 'undefined') immediateError(render); + makeRequest(url, headers, enableInsecure, acceptCodes, render); } }; diff --git a/src/components/InteractiveEditor/EditItem.vue b/src/components/InteractiveEditor/EditItem.vue index 32539351..5be36839 100644 --- a/src/components/InteractiveEditor/EditItem.vue +++ b/src/components/InteractiveEditor/EditItem.vue @@ -226,7 +226,7 @@ export default { if (newItem.hotkey) newItem.hotkey = parseInt(newItem.hotkey, 10); const strToTags = (str) => { const tagArr = str.split(','); - return tagArr.map((tag) => tag.trim().toLowerCase().replace(/[^a-z]+/, '')); + return tagArr.map((tag) => tag.trim().toLowerCase().replace(/[^a-z0-9]+/, '')); }; const strToBool = (str) => { if (str === undefined) return undefined; diff --git a/src/components/LinkItems/Item.vue b/src/components/LinkItems/Item.vue index 024de6f2..c242d1df 100644 --- a/src/components/LinkItems/Item.vue +++ b/src/components/LinkItems/Item.vue @@ -95,6 +95,7 @@ export default { statusCheckUrl: String, // Custom URL for status check endpoint statusCheckInterval: Number, // Num seconds beteween repeating checks statusCheckAllowInsecure: Boolean, // Status check ignore SSL certs + statusCheckAcceptCodes: String, // Allow status checks to pass with a code other than 200 parentSectionTitle: String, // Title of parent section (for add new) isAddNew: Boolean, // Only set if 'fake' item used as Add New button }, @@ -236,7 +237,7 @@ export default { /* Pulls together all user options, returns URL + Get params for ping endpoint */ makeApiUrl() { const { - url, statusCheckUrl, statusCheckHeaders, statusCheckAllowInsecure, + url, statusCheckUrl, statusCheckHeaders, statusCheckAllowInsecure, statusCheckAcceptCodes, } = this; const encode = (str) => encodeURIComponent(str); this.statusResponse = undefined; @@ -249,8 +250,10 @@ export default { ? `&headers=${encode(JSON.stringify(statusCheckHeaders))}` : ''; // Deterimine if user disabled security const enableInsecure = statusCheckAllowInsecure ? '&enableInsecure=true' : ''; + const acceptCodes = statusCheckAcceptCodes ? `&acceptCodes=${statusCheckAcceptCodes}` : ''; // Construct the full API endpoint's URL with GET params - return `${baseUrl}${serviceEndpoints.statusCheck}/${urlToCheck}${headers}${enableInsecure}`; + return `${baseUrl}${serviceEndpoints.statusCheck}/${urlToCheck}` + + `${headers}${enableInsecure}${acceptCodes}`; }, /* Checks if a given service is currently online */ checkWebsiteStatus() { diff --git a/src/components/LinkItems/Section.vue b/src/components/LinkItems/Section.vue index cdb8bf21..56d1f067 100644 --- a/src/components/LinkItems/Section.vue +++ b/src/components/LinkItems/Section.vue @@ -41,6 +41,7 @@ :enableStatusCheck="item.statusCheck !== undefined ? item.statusCheck : enableStatusCheck" :statusCheckInterval="statusCheckInterval" :statusCheckAllowInsecure="item.statusCheckAllowInsecure" + :statusCheckAcceptCodes="item.statusCheckAcceptCodes" @itemClicked="$emit('itemClicked')" @triggerModal="triggerModal" :isAddNew="false" diff --git a/src/components/MinimalView/MinimalSection.vue b/src/components/MinimalView/MinimalSection.vue index 6eb09341..d561f3ca 100644 --- a/src/components/MinimalView/MinimalSection.vue +++ b/src/components/MinimalView/MinimalSection.vue @@ -17,6 +17,8 @@ :itemSize="itemSize" :hotkey="item.hotkey" :enableStatusCheck="shouldEnableStatusCheck(item.statusCheck)" + :statusCheckAllowInsecure="item.statusCheckAllowInsecure" + :statusCheckAcceptCodes="item.statusCheckAcceptCodes" :statusCheckInterval="getStatusCheckInterval()" @itemClicked="$emit('itemClicked')" @triggerModal="triggerModal" diff --git a/src/components/Widgets/NdCpuHistory.vue b/src/components/Widgets/NdCpuHistory.vue index 52ce8ca1..49e74ffe 100644 --- a/src/components/Widgets/NdCpuHistory.vue +++ b/src/components/Widgets/NdCpuHistory.vue @@ -33,9 +33,7 @@ export default { methods: { /* Make GET request to NetData */ fetchData() { - this.makeRequest(this.endpoint).then( - (response) => { this.processData(response); }, - ); + this.makeRequest(this.endpoint).then(this.processData); }, /* Assign data variables to the returned data */ processData(inputData) { diff --git a/src/components/Widgets/NdLoadHistory.vue b/src/components/Widgets/NdLoadHistory.vue index 2d10bb05..3be50f64 100644 --- a/src/components/Widgets/NdLoadHistory.vue +++ b/src/components/Widgets/NdLoadHistory.vue @@ -3,7 +3,6 @@