diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 1dcd2157..10d57bc3 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 🐛 2.0.3 - Bug Fixes [PR #488](https://github.com/Lissy93/dashy/pull/488) +- Press enter to submit login form (Re: #483) +- Allow disabling write to local storage and disk (Re: #485) +- Fix malformed YAML from export config (Re: #482) +- Allow global option for useProxy (Re: #486) +- Look into arrow key navigation error (Re: #463) +- Disallow displaying config (Re: #455) +- Round values in Glances Alerts widget (Re: #454) +- Create a CPU temp widget (Re: #452) +- Add to docs: Keycloak in Kubernetes (Re: #479) +- Add a widget for displaying images (Re: #487) + ## ⬆️ 2.0.2 - Dependency Updates [PR #471](https://github.com/Lissy93/dashy/pull/471) - Updates Alpine version for main Dockerfile - Updates node_modules to latest stable versions diff --git a/.github/workflows/docker-build-publish.yml b/.github/workflows/docker-build-publish.yml index 076f2621..e35048c6 100644 --- a/.github/workflows/docker-build-publish.yml +++ b/.github/workflows/docker-build-publish.yml @@ -1,5 +1,4 @@ -# Builds, scans and tests the multi-architecture docker image -# Then releases it to the DockerHub, GHCR and Quay registries +# Scans, builds and releases a multi-architecture docker image name: 🐳 Build + Publish Multi-Platform Image on: @@ -77,6 +76,9 @@ jobs: username: ${{ secrets.ACR_USERNAME }} password: ${{ secrets.ACR_PASSWORD }} + - name: 🚦 Check Registry Status + uses: crazy-max/ghaction-docker-status@v1 + - name: ⚒️ Build and push uses: docker/build-push-action@v2 with: @@ -87,11 +89,12 @@ jobs: labels: ${{ steps.meta.outputs.labels }} push: true - # - name: 💬 Set Docker Hub Description - # uses: peter-evans/dockerhub-description@v2 - # with: - # repository: lissy93/dashy - # readme-filepath: ./README.md - # short-description: Dashy - A self-hosted start page for your server - # username: ${{ secrets.DOCKER_USERNAME }} - # password: ${{ secrets.DOCKER_PASSWORD }} + - name: 💬 Set Docker Hub Description + uses: peter-evans/dockerhub-description@v2 + with: + repository: lissy93/dashy + readme-filepath: ./docker/docker-readme.md + short-description: Dashy - A self-hosted start page for your server + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + diff --git a/Dockerfile b/Dockerfile index 86ae3f75..ebb6f41c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,10 @@ FROM node:16.13.2-alpine AS BUILD_IMAGE +# Set the platform to build image for ARG TARGETPLATFORM ENV TARGETPLATFORM=${TARGETPLATFORM:-linux/amd64} -# Install additional tools needed on arm64 and armv7 +# Install additional tools needed if on arm64 / armv7 RUN \ case "${TARGETPLATFORM}" in \ 'linux/arm64') apk add --no-cache python3 make g++ ;; \ @@ -23,7 +24,7 @@ COPY . ./ # Build initial app for production RUN yarn build -# Build the final image +# Production stage FROM node:16.13.2-alpine # Define some ENV Vars @@ -44,8 +45,8 @@ COPY --from=BUILD_IMAGE /app ./ ENTRYPOINT [ "/sbin/tini", "--" ] CMD [ "yarn", "build-and-start" ] -# Expose given port +# Expose the port EXPOSE ${PORT} -# Run simple healthchecks every 5 mins, to check the Dashy's everythings great +# Run simple healthchecks every 5 mins, to check that everythings still great HEALTHCHECK --interval=5m --timeout=2s --start-period=30s CMD yarn health-check diff --git a/docker/docker-readme.md b/docker/docker-readme.md new file mode 100644 index 00000000..cab89e30 --- /dev/null +++ b/docker/docker-readme.md @@ -0,0 +1,136 @@ +

Dashy

+

+ Dashy helps you organize your self-hosted services by making them accessible from a single place +
+ +
+ User Showcase | Live Demo | Getting Started | Documentation | GitHub +

+ + Awesome Self-Hosted + + + License MIT + + + Current Version + + + Docker Pulls + + + GitHub Status + + + Known Vulnerabilities + +

+ +## Features 🌈 + +- 🔎 Instant search by name, domain, or tags + customizable hotkeys & keyboard shortcuts +- 🎨 Multiple built-in color themes, with UI color editor and support for custom CSS +- 🧸 Many icon options - Font-Awesome, homelab icons, auto-fetching Favicon, images, emojis, etc. +- 🚦 Status monitoring for each of your apps/links for basic availability and uptime checking +- 📊 Widgets for displaying info and dynamic content from your self-hosted services +- 💂 Optional authentication with multi-user access, configurable privileges, and SSO support +- 🌎 Multi-language support, with 10+ human-translated languages, and more on the way +- ☁ Optional, encrypted, free off-site cloud backup and restore feature available +- 💼 A workspace view, for easily switching between multiple apps simultaneously +- 🛩️ A minimal view, for use as a fast-loading browser Startpage +- 🖱️ Choose app launch method, either new tab, same tab, a pop-up modal, or in the workspace view +- 📏 Customizable layout, sizes, text, component visibility, sort order, behavior, etc. +- 🖼️ Options for a full-screen background image, custom nav-bar links, HTML footer, title, etc. +- 🚀 Easy to setup with Docker, or on bare metal, or with 1-Click cloud deployment +- ⚙️ Easy configuration, either through the UI, or using a YAML file +- ✨ Under active development with improvements and new features added regularly +- 🤏 Small bundle size, fully responsive UI, and PWA for basic offline access +- 🆓 100% free and open-source +- 🔐 Strong focus on privacy +- 🌈 And loads more... + +## Demo ⚡ + +**Live Instances**: [Demo 1](https://demo.dashy.to) (Live Demo) ┆ [Demo 2](https://live.dashy.to) (Dashy Links) ┆ [Demo 3](https://dev.dashy.to) (Dev Preview) + +**Screenshots**: Checkout the [Showcase](https://github.com/Lissy93/dashy/blob/master/docs/showcase.md), to see example dashboards from the community + +**Spin up your own demo**: [![One-Click Deploy with PWD](https://img.shields.io/badge/Play--with--Docker-Deploy-2496ed?style=flat-square&logo=docker)](https://labs.play-with-docker.com/?stack=https://raw.githubusercontent.com/Lissy93/dashy/master/docker-compose.yml) or [`docker run -p 8080:80 lissy93/dashy`](./docs/quick-start.md) + + +

+ Demo +

+ + +**[⬆️ Back to Top](#dashy)** + +--- + +## Getting Started 🛫 + +To deploy Dashy with Docker, just run `docker run -p 8080:80 lissy93/dashy`, then open `http://localhost:8080` + +For full list of options and a Docker compose file, see the [Deployment Docs](https://github.com/Lissy93/dashy/blob/master/docs/deployment.md). + +Dashy can also be run on bare metal using Node.js, or deployed to a cloud service, using the 1-Click deploy script. + +--- + +## Documentation 📝 + +#### Running Dashy +- **[Quick Start](https://github.com/Lissy93/dashy/blob/master/docs/quick-start.md)** - TDLR guide on getting Dashy up and running +- **[Deployment](https://github.com/Lissy93/dashy/blob/master/docs/deployment.md)** - Full guide on deploying Dashy either locally or online +- **[Configuring](https://github.com/Lissy93/dashy/blob/master/docs/configuring.md)** - Complete list of all available options in the config file +- **[App Management](https://github.com/Lissy93/dashy/blob/master/docs/management.md)** - Managing your app, updating, security, web server configuration, etc +- **[Troubleshooting](https://github.com/Lissy93/dashy/blob/master/docs/troubleshooting.md)** - Common errors and problems, and how to fix them + +#### Feature Docs +- **[Authentication](https://github.com/Lissy93/dashy/blob/master/docs/authentication.md)** - Guide to setting up authentication to protect your dashboard +- **[Alternate Views](https://github.com/Lissy93/dashy/blob/master/docs/alternate-views.md)** - Outline of available pages / views and item opening methods +- **[Backup & Restore](https://github.com/Lissy93/dashy/blob/master/docs/backup-restore.md)** - Guide to backing up config with Dashy's cloud sync feature +- **[Icons](https://github.com/Lissy93/dashy/blob/master/docs/icons.md)** - Outline of all available icon types for sections and items, with examples +- **[Language Switching](https://github.com/Lissy93/dashy/blob/master/docs/multi-language-support.md)** - Details on how to switch language, or add a new locale +- **[Status Indicators](https://github.com/Lissy93/dashy/blob/master/docs/status-indicators.md)** - Using Dashy to monitor uptime and status of your apps +- **[Searching & Shortcuts](https://github.com/Lissy93/dashy/blob/master/docs/searching.md)** - Searching, launching methods + keyboard shortcuts +- **[Theming](https://github.com/Lissy93/dashy/blob/master/docs/theming.md)** - Complete guide to applying, writing and modifying themes + styles +- **[Widgets](https://github.com/Lissy93/dashy/blob/master/docs/widgets.md)** - List of all dynamic content widgets, with usage guides and examples + +#### Development and Contributing +- **[Developing](https://github.com/Lissy93/dashy/blob/master/docs/developing.md)** - Running Dashy development server locally, and general workflow +- **[Development Guides](https://github.com/Lissy93/dashy/blob/master/docs/development-guides.md)** - Common development tasks, to help new contributors +- **[Contributing](https://github.com/Lissy93/dashy/blob/master/docs/contributing.md)** - How you can help keep Dashy alive +- **[Showcase](https://github.com/Lissy93/dashy/blob/master/docs/showcase.md)** - See how others are using Dashy, and share your dashboard +- **[Credits](https://github.com/Lissy93/dashy/blob/master/docs/credits.md)** - List of people and projects that have made Dashy possible +- **[Release Workflow](https://github.com/Lissy93/dashy/blob/master/docs/release-workflow.md)** - Info about releases, CI and automated tasks + +--- + +## License 📜 + +Dashy is Licensed under [MIT X11](https://en.wikipedia.org/wiki/MIT_License) + +``` +Copyright © 2021 Alicia Sykes + +Permission is hereby granted, free of charge, to any person obtaining a copy of this +software and associated documentation files (the "Software"), to deal in the Software +without restriction, including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or +substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, Dashy shall not be used in advertising or otherwise +to promote the sale, use, or other dealings in this Software without prior written +authorization from the repo owner. +``` diff --git a/docs/authentication.md b/docs/authentication.md index b429adea..4d64f753 100644 --- a/docs/authentication.md +++ b/docs/authentication.md @@ -74,13 +74,15 @@ For Example: ... ``` -### Security -Since all authentication is happening entirely on the client-side, it is vulnerable to manipulation by an adversary. An attacker could look at the source code, find the function used generate the auth token, then decode the minified JavaScript to find the hash, and manually generate a token using it, then just insert that value as a cookie using the console, and become a logged in user. Therefore, if you need secure authentication for your app, it is strongly recommended to implement this using your web server, or use a VPN to control access to Dashy. The purpose of the login page is merely to prevent immediate unauthorized access to your homepage. +### Permissions +Any user who is not an admin (with `type: admin`) will not be able to write changes to disk. -Addressing this is on the todo list, and there are several potential solutions: -1. Encrypt all site data against the users password, so that an attacker can not physically access any data without the correct decryption key -2. Use a backend service to handle authentication and configuration, with no user data returned from the server until the correct credentials are provided. However, this would require either Dashy to be run using it's Node.js server, or the use of an external service -3. ~~Implement authentication using a self-hosted identity management solution, such as [Keycloak for Vue](https://www.keycloak.org/securing-apps/vue)~~ **This is now implemented, and released in PR #174 of V 1.6.5!** +You can also prevent any user from writing changes to disk, using `preventWriteToDisk`. Or prevent any changes from being saved locally in browser storage, using `preventLocalSave`. Both properties can be found under [`appConfig`](./docs/configuring.md#appconfig-optional). + +To disable all UI config features, including View Config, set `disableConfiguration`. + +### Security +With basic auth, all logic is happening on the client-side, which could mean a skilled user could manipulate the code to view parts of your configuration, including the hash. If the SHA-256 hash is of a common password, it may be possible to determine it, using a lookup table, in order to find the original password. Which can be used to manually generate the auth token, that can then be inserted into session storage, to become a valid logged in user. Therefore, you should always use a long, strong and unique password, and if you instance contains security-critical info and/ or is exposed directly to the internet, and alternative authentication method may be better. The purpose of the login page is merely to prevent immediate unauthorized access to your homepage. **[⬆️ Back to Top](#authentication)** diff --git a/docs/configuring.md b/docs/configuring.md index 64b5028b..71356c3d 100644 --- a/docs/configuring.md +++ b/docs/configuring.md @@ -1,45 +1,54 @@ # Configuring -All app configuration is specified in [`/public/conf.yml`](https://github.com/Lissy93/dashy/blob/master/public/conf.yml) which is in [YAML Format](https://yaml.org/) format. Changes can also be made [directly through the UI](#editing-config-through-the-ui) and previewed live, from here you can also export, backup, reset, validate and download your configuration file. +All app configuration is specified in [`/public/conf.yml`](https://github.com/Lissy93/dashy/blob/master/public/conf.yml) which is in [YAML Format](https://yaml.org/) format. If you're using Docker, this file can be passed in as a volume. Changes can either be made directly to this file, or done [through the UI](#editing-config-through-the-ui). From the UI you can also export, backup, reset, validate and download your configuration file. + +#### There are three ways to edit the config +- **Directly in the YAML file** _(5/5 reliability, 3/5 usability)_ + - Write changes directly to the conf.yml file, optionally using one of the templates provided. This can be done in your favorite editor and uploading to your server, or directly editing the file via SSH, but the easiest method would be to use [Code Server](https://github.com/coder/code-server) +- **UI JSON Editor** _(4/5 reliability, 4/5 usability)_ + - From the UI, under the config menu there is a JSON editor, with built-in validation, documentation and advanced options +- **UI Visual Editor** _(3/5 reliability, 5/5 usability)_ + - From the UI, enter the Interactive Edit Mode, then click any part of the page to edit. Changes are previewed live, and then saved to disk +- **REST API** _(Coming soon)_ + - Programmatically edit config either through the command line, using a script or a third-party application + +#### Tips +- You may find it helpful to look at some sample config files to get you started, a collection of which can be found [here](https://gist.github.com/Lissy93/000f712a5ce98f212817d20bc16bab10) +- You can check that your config file fits the schema, by running `yarn validate-config` +- After modifying your config, the app needs to be recompiled, by running `yarn build` - this happens automatically if you're using Docker +- It is recommended to keep a backup of your config file. You can download it under Config menu, or use the [Cloud Backup](./docs/backup-restore.md) feature. +- You can make use of YAML features, like anchors, comments, multi-line strings, etc to reuse attributes and keep your config file readable +- Once you have finished configuring your dashboard, you can choose to [disable UI config](#preventing-changes) if you wish +- All fields are optional, unless otherwise stated. The following file provides a reference of all supported configuration options. ---- - #### Contents - [**`pageInfo`**](#pageinfo) - Header text, footer, title, navigation, etc - - [`navLinks`](#pageinfonavlinks-optional) - Navigation bar items and links + - [`navLinks`](#pageinfonavlinks-optional) - Links to display in the navigation bar - [**`appConfig`**](#appconfig-optional) - Main application settings - - [`webSearch`](#appconfigwebsearch-optional) - Configure web search engine options - - [`hideComponents`](#appconfighidecomponents-optional) - Show/ hide page components - - [`auth`](#appconfigauth-optional) - Built-in authentication setup - - [`users`](#appconfigauthusers-optional) - Setup for simple auth - - [`keycloak`](#appconfigauthkeycloak-optional) - Auth using Keycloak + - [`webSearch`](#appconfigwebsearch-optional) - Configure web search engine options + - [`hideComponents`](#appconfighidecomponents-optional) - Show/ hide page components + - [`auth`](#appconfigauth-optional) - Built-in authentication setup + - [`users`](#appconfigauthusers-optional) - List or users (for simple auth) + - [`keycloak`](#appconfigauthkeycloak-optional) - Auth config for Keycloak - [**`sections`**](#section) - List of sections - - [`displayData`](#sectiondisplaydata-optional) - Section display settings - - [`icon`](#sectionicon-and-sectionitemicon) - Icon for a section - - [`items`](#sectionitem) - List of items - - [`icon`](#sectionicon-and-sectionitemicon) - Icon for an item + - [`displayData`](#sectiondisplaydata-optional) - Section display settings + - [`show/hideForKeycloakUsers`](#sectiondisplaydatahideforkeycloakusers-and-sectiondisplaydatashowforkeycloakusers) - Set user controls + - [`icon`](#sectionicon-and-sectionitemicon) - Icon for a section + - [`items`](#sectionitem) - List of items + - [`icon`](#sectionicon-and-sectionitemicon) - Icon for an item + - [`widgets`](#sectionwidget-optional) - List of widgets - [**Notes**](#notes) - [Editing Config through the UI](#editing-config-through-the-ui) - - [About YAML](#about-yaml) - - [Config Saving Methods](#config-saving-methods) - - [Preventing Changes](#preventing-changes-being-written-to-disk) + - [About YAML](#about-yaml) + - [Config Saving Methods](#config-saving-methods) + - [Preventing Changes](#preventing-changes) - [Example](#example) --- -Tips: -- You may find it helpful to look at some sample config files to get you started, a collection of which can be found [here](https://gist.github.com/Lissy93/000f712a5ce98f212817d20bc16bab10) -- You can check that your config file fits the schema, by running `yarn validate-config` -- After modifying your config, the app needs to be recompiled, by running `yarn build` - this happens automatically whilst the app is running if you're using Docker -- It is recommended to make and keep a backup of your config file. You can download your current config through the UI either from the Config menu, or using the `/download` endpoint. Alternatively, you can use the [Cloud Backup](./docs/backup-restore.md) feature. -- The config can also be modified directly through the UI, validated and written to the conf.yml file. -- All fields are optional, unless otherwise stated. - ---- - ### Top-Level Fields **Field** | **Type** | **Required**| **Description** @@ -98,7 +107,10 @@ Tips: **`routingMode`** | `string` | _Optional_ | Can be either `hash` or `history`. Determines the URL format for sub-pages, hash mode will look like `/#/home` whereas with history mode available you have nice clean URLs, like `/home`. For more info, see the [Vue docs](https://router.vuejs.org/guide/essentials/history-mode.html#example-server-configurations). If you're hosting Dashy with a custom BASE_URL, you will find that a bit of extra server config is necessary to get history mode working, so here you may want to instead use `hash` mode.Defaults to `history`. **`enableMultiTasking`** | `boolean` | _Optional_ | If set to true, will keep apps open in the background when in the workspace view. Useful for quickly switching between multiple sites, and preserving their state, but comes at the cost of performance. **`workspaceLandingUrl`** | `string` | _Optional_ | The URL or an app, service or website to launch when the workspace view is opened, before another service has been launched -**`allowConfigEdit`** | `boolean` | _Optional_ | Should prevent / allow the user to write configuration changes to the conf.yml from the UI. When set to `false`, the user can only apply changes locally using the config editor within the app, whereas if set to `true` then changes can be written to disk directly through the UI. Defaults to `true`. Note that if authentication is enabled, the user must be of type `admin` in order to apply changes globally. +**`preventWriteToDisk`** | `boolean` | _Optional_ | If set to `true`, users will be prevented from saving config changes to disk through the UI +**`preventLocalSave`** | `boolean` | _Optional_ | If set to `true`, users will be prevented from applying config changes to local storage +**`disableConfiguration`** | `boolean` | _Optional_ | If set to true, no users will be able to view or edit the config through the UI +**`widgetsAlwaysUseProxy`** | `boolean` | _Optional_ | If set to `true`, requests made by widgets will always be proxied, same as setting `useProxy: true` on each widget. Note that this may break some widgets. **`showSplashScreen`** | `boolean` | _Optional_ | If set to `true`, a loading screen will be shown. Defaults to `false`. **`enableErrorReporting`** | `boolean` | _Optional_ | Enable reporting of unexpected errors and crashes. This is off by default, and **no data will ever be captured unless you explicitly enable it**. Turning on error reporting helps previously unknown bugs get discovered and fixed. Dashy uses [Sentry](https://github.com/getsentry/sentry) for error reporting. Defaults to `false`. **`sentryDsn`** | `boolean` | _Optional_ | If you need to monitor errors in your instance, then you can use Sentry to collect and process bug reports. Sentry can be self-hosted, or used as SaaS, once your instance is setup, then all you need to do is pass in the DSN here, and enable error reporting. You can learn more on the [Sentry DSN Docs](https://docs.sentry.io/product/sentry-basics/dsn-explainer/). Note that this will only ever be used if `enableErrorReporting` is explicitly enabled. @@ -280,8 +292,13 @@ When updating the config through the JSON editor in the UI, you have two save op - Changes saved locally will only be applied to the current user through the browser, and will not apply to other instances - you either need to use the cloud sync feature, or manually update the conf.yml file. - On the other-hand, if you choose to write changes to disk, then your main `conf.yml` file will be updated, and changes will be applied to all users, and visible across all devices. For this functionality to work, you must be running Dashy with using the Docker container, or the Node server. A backup of your current configuration will also be saved in the same directory. -### Preventing Changes being Written to Disk -To disallow any changes from being written to disk via the UI config editor, set `appConfig.allowConfigEdit: false`. If you are using users, and have setup `auth` within Dashy, then only users with `type: admin` will be able to write config changes to disk. +### Preventing Changes + +If you have authentication set up, then any user who is not an admin (with `type: admin`) will not be able to write changes to disk. + +You can also prevent changes fro any user being written to disk, using `preventWriteToDisk`. Or prevent any changes from being saved locally in browser storage, using `preventLocalSave`. + +To disable all UI config features, set `disableConfiguration`. ### Example diff --git a/docs/development-guides.md b/docs/development-guides.md index 07edd838..4f2256c8 100644 --- a/docs/development-guides.md +++ b/docs/development-guides.md @@ -11,6 +11,7 @@ Sections: - [Hiding Page Furniture](#hiding-page-furniture-on-certain-routes) - [Adding / Using Environmental Variables](#adding--using-environmental-variables) - [Building a Widget](#building-a-widget) +- [Respecting Config Permissions](#respecting-config-permissions) ## Creating a new theme @@ -94,18 +95,34 @@ If you are not comfortable with making pull requests, or do not want to modify t # Adding a new option in the config file -This section is for, if you're adding a new component or setting, that requires an additional item to be added to the users config file. +This section is for, adding a new setting to the config file. All of the users config is specified in `./public/conf.yml` - see [Configuring Docs](./configuring.md) for info. -Before adding a new option in the config file, first ensure that there is nothing similar available, that is is definitely necessary, it will not conflict with any other options and most importantly that it will not cause any breaking changes. Ensure that you choose an appropriate and relevant section to place it under. +It's important to first ensure that there isn't a similar option already available, the new option is definitely necessary, and most importantly that it is fully backwards compatible. -Next decide the most appropriate place for your attribute: +Next choose the appropriate section to place it under - Application settings should be located under `appConfig` - Page info (such as text and metadata) should be under `pageInfo` - Data relating to specific sections should be under `section[n].displayData` -- And for setting applied to specific items, it should be under `item[n]` +- Settings applied to specific items or widgets, should be under `item[n]` or `widget[n]` -In order for the user to be able to add your new attribute using the Config Editor, and for the build validation to pass, your attribute must be included within the [ConfigSchema](https://github.com/Lissy93/dashy/blob/master/src/utils/ConfigSchema.js). You can read about how to do this on the [ajv docs](https://ajv.js.org/json-schema.html). Give your property a type and a description, as well as any other optional fields that you feel are relevant. For example: +For example, if your option is added under `appConfig`, you can access it within your component using the `$store`, this is typically placed in a computed property, e.g: + +```javascript +computed: { + appConfig() { + return this.$store.getters.appConfig; + }, + ... +}, +``` + +Then, where you want get the users value within your component, use something like: `this.appConfig.myProperty`. Don't forget to have a fallback or default for then the user hasn't specified it. + +If you have a default fallback value, then this would typically be specified in the [`defaults.js`](https://github.com/Lissy93/dashy/blob/master/src/utils/defaults.js) file. + +You will now need to add the definition of your new attribute into the [ConfigSchema](https://github.com/Lissy93/dashy/blob/master/src/utils/ConfigSchema.js). This will make it available in the UI config editor, and also ensure that the config validation check doesn't fail. +For example: ```json "fontAwesomeKey": { @@ -124,24 +141,21 @@ or } ``` -Next, if you're property should have a default value, then add it to [`defaults.js`](https://github.com/Lissy93/dashy/blob/master/src/utils/defaults.js). This ensures that nothing will break if the user does not use your property, and having all defaults together keeps things organised and easy to manage. - -If your property needs additional logic for fetching, setting or processing, then you can add a helper function within [`ConfigHelpers.js`](https://github.com/Lissy93/dashy/blob/master/src/utils/ConfigHelpers.js). - Finally, add your new property to the [`configuring.md`](./configuring.md) API docs. Put it under the relevant section, and be sure to include field name, data type, a description and mention that it is optional. If your new feature needs more explaining, then you can also document it under the relevant section elsewhere in the documentation. Checklist: - [ ] Ensure the new attribute is actually necessary, and nothing similar already exists - [ ] Update the [Schema](https://github.com/Lissy93/dashy/blob/master/src/utils/ConfigSchema.js) with the parameters for your new option -- [ ] Set a default value (if required) within [`defaults.js`](https://github.com/Lissy93/dashy/blob/master/src/utils/defaults.js) -- [ ] Document the new value in [`configuring.md`](./configuring.md) -- [ ] Test that the reading of the new attribute is properly handled, and will not cause any errors when it is missing or populated with an unexpected value +- [ ] If required, set a default or fallback value (usually in [`defaults.js`](https://github.com/Lissy93/dashy/blob/master/src/utils/defaults.js)) +- [ ] Document the new value in [`configuring.md`](./configuring.md), and if required under the relevant section in the docs +- [ ] Ensure your changes are backwards compatible, and that nothing breaks if the attribute isn't specified --- ## Updating Dependencies -Running `yarn upgrade` will updated all dependencies based on the ranges specified in the `package.json`. The `yarn.lock` file will be updated, as will the contents of `./node_modules`, for more info, see the [yarn upgrade documentation](https://classic.yarnpkg.com/en/docs/cli/upgrade/). It is important to thoroughly test after any big dependency updates. +Running `yarn upgrade` will updated all dependencies based on the ranges specified in the `package.json`. The `yarn.lock` file will be updated, as will the contents of `./node_modules`, for more info, see the [yarn upgrade documentation](https://classic.yarnpkg.com/en/docs/cli/upgrade/). [`npm-check-updates`](https://github.com/raineorshine/npm-check-updates) is a useful tool to help with this. +It is important to thoroughly test after any big dependency updates. --- @@ -430,3 +444,31 @@ Finally, add some documentation for your widget in the [Widget Docs](https://git **Summary**: For a complete example of everything discussed here, see: [`3da76ce`](https://github.com/Lissy93/dashy/commit/3da76ce2999f57f76a97454c0276301e39957b8e) + +--- + +## Respecting Config Permissions + +Any screen that displays part or all of the users config, must not be shown when the user has disabled viewing config. + +This can be done by checking the `allowViewConfig` attribute of the `permissions` getter, in the store. +First create a new `computed` property, like: +``` +allowViewConfig() { + return this.$store.getters.permissions.allowViewConfig; +}, +``` + +Then wrap the part of your UI which displays config with: `v-if="allowViewConfig"` + +If required, add a message showing that the component isn't available, using the `AccessError` component. E.g. + +``` +import AccessError from '@/components/Configuration/AccessError'; +``` + +``` + +``` + +The `$store.getters.permissions` object also returns options for when and where config can be saved, using: `allowWriteToDisk`, and `allowSaveLocally` - both are booleans. diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 6b07cb62..c60cc4dc 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -22,6 +22,7 @@ - [Status Checks Failing](#status-checks-failing) - [Diagnosing Widget Errors](#widget-errors) - [Fixing Widget CORS Errors](#widget-cors-errors) +- [Keycloak Redirect Error](#keycloak-redirect-error) - [How-To Open Browser Console](#how-to-open-browser-console) - [Git Contributions not Displaying](#git-contributions-not-displaying) @@ -273,6 +274,25 @@ For testing purposes, you can use an addon, which will disable the CORS checks. --- +## Keycloak Redirect Error + +Firstly, ensure that in your Keycloak instance you have populated the Valid Redirect URIs field ([screenshot](https://user-images.githubusercontent.com/1862727/148599768-db4ee4f8-72c5-402d-8f00-051d999e6267.png)) with the URL to your Dashy instance. + +You may need to specify CORS headers on your Keycloak instance, to allow requests coming from Dashy, e.g: + +``` +Access-Control-Allow-Origin: https://dashy.example.com +``` + +If you're running in Kubernetes, you will need to enable CORS ingress rules, see [docs](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#enable-cors), e.g: + +``` +nginx.ingress.kubernetes.io/cors-allow-origin: "https://dashy.example.com" +nginx.ingress.kubernetes.io/enable-cors: "true" +``` + +--- + ## How-To Open Browser Console When raising a bug, one crucial piece of info needed is the browser's console output. This will help the developer diagnose and fix the issue. diff --git a/docs/widgets.md b/docs/widgets.md index 131f7075..1059d7ad 100644 --- a/docs/widgets.md +++ b/docs/widgets.md @@ -12,6 +12,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a - [Weather](#weather) - [Weather Forecast](#weather-forecast) - [RSS Feed](#rss-feed) + - [Image](#image) - [Public IP Address](#public-ip) - [Crypto Watch List](#crypto-watch-list) - [Crypto Price History](#crypto-token-price-history) @@ -57,6 +58,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a - [Network Traffic](#network-traffic) - [Resource Usage Alerts](#resource-usage-alerts) - [Public & Private IP](#ip-address) + - [CPU Temperature](#cpu-temp) - **[Dynamic Widgets](#dynamic-widgets)** - [Iframe Widget](#iframe-widget) - [HTML Embed Widget](#html-embedded-widget) @@ -209,6 +211,41 @@ Display news and updates from any RSS-enabled service. --- +### Image + +Displays an image. + +This may be useful if you have a service (such as Grafana - [see example](https://mattionline.de/grafana-api-export-graph-as-png/)), which periodically exports charts or other data as an image. + +You can also store images within Dashy's public directory (using a Docker volume), and reference them directly. E.g. `-v ./path/to/my-homelab-logo.png:/app/public/logo.png`, then in the widget `imagePath: /logo.png`. + +Similarly, any web service that serves up widgets as image can be used. E.g. you could show current star chart for a GitHub repo, with: `imagePath: https://starchart.cc/Lissy93/dashy.svg`. + +If you'd like to embed a live screenshot, of all or just part of a website, then this can be done using [API Flash](https://apiflash.com/). + +Or what about showing a photo of the day? Try `https://source.unsplash.com/random/400x300` or `https://picsum.photos/400/300` + +

+ +##### Options + +**Field** | **Type** | **Required** | **Description** +--- | --- | --- | --- +**`imagePath`** | `string` | Required | The path (local or remote) of the image to display + +##### Example + +```yaml +- type: image + options: + imagePath: https://i.ibb.co/yhbt6CY/dashy.png +``` + +##### Info +Unless image fetched from remote source, no external data request is made. + +--- + ### Public IP Often find yourself searching "What's my IP", just so you can check your VPN is still connected? This widget displays your public IP address, along with ISP name and approx location. Data can be fetched from either [IpApi.co](https://ipapi.co/), [IP-API.com](https://ip-api.com/) or [IpGeolocation.io](https://ipgeolocation.io/). @@ -1488,6 +1525,25 @@ Shows public and private IP address. Note that the ip plugin is not available on --- +### CPU Temp + +Displays temperature data from system CPUs. + +Note: This widget uses the [`sensors`](https://github.com/nicolargo/glances/blob/develop/glances/plugins/glances_sensors.py) plugin, which is disabled by default, and may cause [performance issues](https://github.com/nicolargo/glances/issues/1664#issuecomment-632063558). +You'll need to enable the sensors plugin to use this widget, using: `--enable-plugin sensors` when you start Glances. + +

+ +##### Example + +```yaml +- type: gl-cpu-temp + options: + hostname: http://192.168.130.2:61208 +``` + +--- + ## Dynamic Widgets ### Iframe Widget @@ -1556,6 +1612,14 @@ Or scriptSrc: 'https://files.coinmarketcap.com/static/widget/currency.js' ``` +You can also use this widget to display an image, wither locally or from a remote origin. + +```yaml +- type: embed + options: + html: '' +``` + --- ### API Response @@ -1786,4 +1850,4 @@ For testing purposes, you can use an addon, which will disable the CORS checks. ### Raising an Issue -If you need to submit a bug report for a failing widget, then please include the full console output (see [how](/docs/troubleshooting.md#how-to-open-browser-console)) as well as the relevant parts of your config file. Before sending the request, ensure you've read the docs. If you're new to GitHub, an haven't previously contributed to the project, then please fist star the repo to avoid your ticket being closed by the anti-spam bot. \ No newline at end of file +If you need to submit a bug report for a failing widget, then please include the full console output (see [how](/docs/troubleshooting.md#how-to-open-browser-console)) as well as the relevant parts of your config file. Before sending the request, ensure you've read the docs. If you're new to GitHub, an haven't previously contributed to the project, then please fist star the repo to avoid your ticket being closed by the anti-spam bot. diff --git a/package.json b/package.json index 8a60cc1b..adbdd615 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Dashy", - "version": "2.0.2", + "version": "2.0.3", "license": "MIT", "main": "server", "author": "Alicia Sykes (https://aliciasykes.com)", @@ -64,9 +64,6 @@ "vue-svg-loader": "^0.16.0", "vue-template-compiler": "^2.6.14" }, - "gitHooks": { - "pre-commit": "yarn lint" - }, "engines": { "node": ">=16.0.0" }, diff --git a/services/config-validator.js b/services/config-validator.js index b56c4d35..48c1e827 100644 --- a/services/config-validator.js +++ b/services/config-validator.js @@ -1,8 +1,11 @@ -/* eslint-disable no-console */ -/* Script that validates the conf.yml file against Dashy's schema, and outputs any issues */ -const Ajv = require('ajv'); -const yaml = require('js-yaml'); -const fs = require('fs'); +/** + * Checks that conf.yml is present + parsable, then validates it against the schema + * Prints detailed info about any errors or warnings to help the user fix the issue + */ + +const fs = require('fs'); // For opening + reading files +const yaml = require('js-yaml'); // For parsing YAML +const Ajv = require('ajv'); // For validating with schema const schema = require('../src/utils/ConfigSchema.json'); @@ -17,32 +20,29 @@ const validatorOptions = { const ajv = new Ajv(validatorOptions); /* Message printed when validation was successful */ -const successMsg = () => '\x1b[1m\x1b[32mNo issues found, your configuration is valid :)\x1b[0m\n'; +const successMsg = () => '\x1b[1m\x1b[32m✔️ Config file is valid, no issues found\x1b[0m\n'; /* Just a wrapper to system's console.log */ -const logToConsole = (msg) => { console.log(msg); }; +const logToConsole = (msg) => { console.log(msg || '\n'); }; // eslint-disable-line no-console /* Formats error message. ready for printing to the console */ const errorMsg = (output) => { const warningFont = '\x1b[103m\x1b[34m'; const line = `${warningFont}${new Array(42).fill('━').join('')}\x1b[0m`; + const formatParams = (params) => { + if (params.additionalProperty) return `(${params.additionalProperty})`; + return ''; + }; let msg = `\n${line}\n${warningFont} Warning: ${output.length} ` + `issue${output.length > 1 ? 's' : ''} found in config file \x1b[0m\n${line}\n`; output.forEach((details, index) => { - msg += `${'\x1b[36m'}${index + 1}. ${details.keyword} ${details.message} ` - + `in ${details.instancePath}\x1b[0m\n`; + msg += `${'\x1b[36m'}${index + 1}. \x1b[4m${details.instancePath}\x1b[0m\x1b[36m ` + + `${details.message} ${formatParams(details.params)}\x1b[0m\n`; }); return msg; }; -/* Error message printed when the file could not be opened */ -const bigError = () => { - const formatting = '\x1b[30m\x1b[43m'; - const line = `${formatting}${new Array(38).fill('━').join('')}\x1b[0m\n`; - const msg = `${formatting} Error, unable to validate 'conf.yml' \x1b[0m\n`; - return `\n${line}${msg}${line}\n`; -}; - +/* Sets valid status as environmental variable */ const setIsValidVariable = (isValid) => { process.env.VUE_APP_CONFIG_VALID = isValid; }; @@ -60,16 +60,49 @@ const validate = (config) => { } }; -try { +/* Error message printed when the file could not be opened */ +const bigError = () => { + const formatting = '\x1b[30m\x1b[43m'; + const line = `${formatting}${new Array(38).fill('━').join('')}\x1b[0m\n`; + const msg = `${formatting} Error, unable to validate 'conf.yml' \x1b[0m\n`; + return `\n${line}${msg}${line}`; +}; + +/* Given an error object, prints helpful info to the user */ +const printFileReadError = (e) => { + let customError = ''; + if (e.mark) { // YAML syntax error + customError = `\x1b[33m\x1b[4m⚠️ Error on line ${e.mark.line}, column ${e.mark.column}: ` + + `${e.reason}\x1b[0m\n\n${e.mark.snippet}\n\x1b[0m` + + '\n\x1b[36m ℹ️ You might find it helpful to use a YAML validator' + + ', like: \x1b[4mhttps://yamlchecker.com/\x1b[0m\n'; + } + if (e.code === 'ENOENT') { // File not found error + customError = `\x1b[33m⚠️ Config file could not be found at ${e.path}\x1b[0m\n`; + } + if (e.code === 'EISDIR') { // Not a file + customError = '\x1b[33m⚠️ Config needs to be a file, but found a directory instead \x1b[0m\n'; + } + if (e.code === 'EACCES' || e.code === 'EPERM') { // File permissions error + customError = '\x1b[33m⚠️ Permission denied \x1b[0m\n'; + } + logToConsole(customError); + if (customError === '') { // Unknown error, print stack trace + const moreInfo = 'Ensure that your config file is present, readable, and valid YAML. ' + + 'If this issue persists, you can get support by raising a ticket on GitHub. ' + + 'Please include the following stack trace'; + logToConsole(moreInfo); + // eslint-disable-next-line no-console + console.warn('\x1b[33mStack Trace for config-validator.js:\x1b[0m\n', e); + logToConsole(); + } +}; + +try { // Try to open and parse the YAML file const config = yaml.load(fs.readFileSync('./public/conf.yml', 'utf8')); validate(config); } catch (e) { // Something went very wrong... setIsValidVariable(false); logToConsole(bigError()); - logToConsole('Please ensure that your config file is present, ' - + 'has the correct access rights and is parsable. ' - + 'If this warning persists, it may be an issue with the ' - + 'validator function. Please raise an issue, and include the following stack trace:\n'); - console.warn('\x1b[33mStack Trace for config-validator.js:\x1b[0m\n', e); - logToConsole('\n\n'); + printFileReadError(e); } diff --git a/src/components/Configuration/AccessError.vue b/src/components/Configuration/AccessError.vue new file mode 100644 index 00000000..4b928b3d --- /dev/null +++ b/src/components/Configuration/AccessError.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/src/components/Configuration/AppInfoModal.vue b/src/components/Configuration/AppInfoModal.vue index 7182592c..4803d7f0 100644 --- a/src/components/Configuration/AppInfoModal.vue +++ b/src/components/Configuration/AppInfoModal.vue @@ -30,7 +30,8 @@ Security Policy

License

- Licensed under MIT X11. Copyright Alicia Sykes © 2021.
+ Licensed under MIT X11. + Copyright Alicia Sykes © 2021.
For licenses for third-party modules, please see Legal.
For the full list of contributors and thanks, see Credits. diff --git a/src/components/Configuration/ConfigContainer.vue b/src/components/Configuration/ConfigContainer.vue index c797ff90..a610ebe0 100644 --- a/src/components/Configuration/ConfigContainer.vue +++ b/src/components/Configuration/ConfigContainer.vue @@ -1,49 +1,61 @@