Merge pull request #26 from Lissy93/improved-docker-deployments

Improved docker deployments
This commit is contained in:
Alicia Sykes 2021-06-11 17:40:58 +01:00 committed by GitHub
commit cd274e2884
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
77 changed files with 1211 additions and 863 deletions

View File

@ -25,3 +25,6 @@ EXPOSE ${PORT}
# Finally, run start command to serve up the built application
CMD [ "yarn", "build-and-start"]
# Run simple healthchecks every 5 mins, to check the Dashy's everythings great
HEALTHCHECK --interval=5m --timeout=2s --start-period=30s CMD yarn health-check

View File

@ -29,6 +29,8 @@
**Live Demos**: [Demo 1](https://dashy-demo-1.as93.net) ┆ [Demo 2](https://dashy-demo-2.as93.net) ┆ [Demo 3](https://dashy-demo-3.as93.net)
**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)
**Screenshots**
![Screenshots](https://i.ibb.co/r5T3MwM/dashy-screenshots.png)
@ -42,19 +44,20 @@
## Getting Started 🛫
> For full setup instructions, see: [**Getting Started**](./docs/getting-started.md)
#### Deploying from Docker Hub 🐳
You will need [Docker](https://docs.docker.com/get-docker/) installed on your system
```docker
docker run -d \
-p 8080:80 \
-p 4000:80 \
-v /root/my-local-conf.yml:/app/public/conf.yml \
--name my-dashboard \
--restart=always \
lissy93/dashy:latest
```
After making changes to your configuration file, you will need to run: `docker exec -it [container-id] yarn build` to rebuild. You can also run other commands, such as `yarn validate-config` this way too. Container ID can be found by running `docker ps`.
After making changes to your configuration file, you will need to run: `docker exec -it [container-id] yarn build` to rebuild. You can also run other commands, such as `yarn validate-config` this way too. Container ID can be found by running `docker ps`. Healthchecks are pre-configured to monitor the uptime and response times of Dashy, and the status of which can be seen in the container logs, e.g. `docker inspect --format "{{json .State.Health }}" [container-id]`.
#### Deploying from Source 🚀
@ -76,7 +79,9 @@ After making changes to your configuration file, you will need to run: `yarn bui
Dashy is configured with a single [YAML](https://yaml.org/) file, located at `./public/conf.yml` (or `./app/public/conf.yml` for Docker). Any other optional user-customizable assets are also located in the `./public/` directory, e.g. `favicon.ico`, `manifest.json`, `robots.txt` and `web-icons/*`. If you are using Docker, the easiest way to method is to mount a Docker volume (e.g. `-v /root/my-local-conf.yml:/app/public/conf.yml`)
In the production environment, the app needs to be rebuilt in order for changes to take effect. This can be done with `yarn build`, or `docker exec -it [container-id] yarn build` if you are using Docker (where container ID can be found by running `docker ps`). You can check that your config matches Dashy's [schema](https://github.com/Lissy93/dashy/blob/master/src/utils/ConfigSchema.json) before deploying, by running `yarn validate-config.`
In the production environment, the app needs to be rebuilt in order for changes to take effect. This can be done with `yarn build`, or `docker exec -it [container-id] yarn build` if you are using Docker (where container ID can be found by running `docker ps`).
You can check that your config matches Dashy's [schema](https://github.com/Lissy93/dashy/blob/master/src/utils/ConfigSchema.json) before deploying, by running `yarn validate-config.`
You may find these [example config](https://gist.github.com/Lissy93/000f712a5ce98f212817d20bc16bab10) helpful for getting you started
@ -86,7 +91,11 @@ You may find these [example config](https://gist.github.com/Lissy93/000f712a5ce9
> For full configuration documentation, see: [**Theming**](./docs/theming.md)
<p align="right"><img src="https://i.ibb.co/BVSHV1v/dashy-themes-slideshow.gif" width="400"></p>
<p align="center">
<a href="https://i.ibb.co/BVSHV1v/dashy-themes-slideshow.gif">
<img alt="Example Themes" src="/docs/assets/theme-slideshow.gif" width="400">
</a>
</p>
The app comes with a number of built-in themes, but it's also easy to write you're own. All colors, and most other CSS properties make use of CSS variables, which makes customizing the look and feel of Dashy very easy.
@ -131,7 +140,7 @@ Some ideas for PRs include: bug fixes, improve the docs, add new themes, impleme
Before you submit your pull request, please ensure the following:
- Must be backwards compatible
- All lint checks and tests must pass
- If a new option in the the config file is added, it needs to be added into the schema, and documented in the configuring guide
- If a new option in the the config file is added, it needs to be added into the [schema](https://github.com/Lissy93/dashy/blob/master/src/utils/ConfigSchema.json), and documented in the [configuring](https://github.com/Lissy93/dashy/blob/master/docs/configuring.md) guide
- If a new dependency is required, it must be essential, and it must be thoroughly checked out for security or efficiency issues
- Your pull request will need to be up-to-date with master, and the PR template must be filled in
@ -139,6 +148,8 @@ Before you submit your pull request, please ensure the following:
## Support 🙋‍♀️
> For general discussions, the [Discussions Board](https://github.com/Lissy93/dashy/discussions) is now active!
If you've found a bug, or something that isn't working as you'd expect, please raise an issue, so that it can be resolved. Similarly, if you're having trouble getting things up and running, feel free to ask a question. Feature requests and feedback are also welcome, as it helps Dashy improve.
- [Raise a Bug 🐛](https://github.com/Lissy93/dashy/issues/new?assignees=Lissy93&labels=%F0%9F%90%9B+Bug&template=bug-report---.md&title=%5BBUG%5D)
@ -146,9 +157,9 @@ If you've found a bug, or something that isn't working as you'd expect, please r
- [Ask a Question 🤷‍♀️](https://github.com/Lissy93/dashy/issues/new?assignees=Lissy93&labels=%F0%9F%A4%B7%E2%80%8D%E2%99%82%EF%B8%8F+Question&template=question------.md&title=%5BQUESTION%5D)
- [Share Feedback 🌈](https://github.com/Lissy93/dashy/issues/new?assignees=&labels=%F0%9F%8C%88+Feedback&template=share-feedback---.md&title=%5BFEEDBACK%5D)
For general questions about any of the technologies used, you should search the [web](https://duckduckgo.com), or open a question on [StackOverflow](https://stackoverflow.com/questions/)
For more general questions about any of the technologies used, [StackOverflow](https://stackoverflow.com/questions/) may be more helpful first port of info
If you need to get in touch securely with the author me, you can send any messages to me at:
If you need to get in touch securely with the author (me, Alicia Sykes), drop me a message at:
- **Email**: `alicia at omg dot lol`
- **Public Key** [`0688 F8D3 4587 D954 E9E5 1FB8 FEDB 68F5 5C02 83A7`](https://keybase.io/aliciasykes/pgp_keys.asc?fingerprint=0688f8d34587d954e9e51fb8fedb68f55c0283a7)
@ -201,6 +212,9 @@ At it's core, the application uses [Vue.js](https://github.com/vuejs/vue), as we
##### Backup & Sync Server
Although the app is purely frontend, there is an optional cloud backup and restore feature. This is built as a serverless function on [Cloudflare workers](https://workers.cloudflare.com/) using [KV](https://developers.cloudflare.com/workers/runtime-apis/kv) and [web crypto](https://developers.cloudflare.com/workers/runtime-apis/web-crypto)
##### External Services
The 1-Click deploy demo uses [Play-with-Docker Labs](https://play-with-docker.com/). Code is hosted on [GitHub](https://github.com), Docker image is hosted on [DockerHub](https://hub.docker.com/), and the demos are hosted on [Netlify](https://www.netlify.com/).
### Alternatives 🙌
There are a few self-hosted web apps, that serve a similar purpose to Dashy. If you're looking for a dashboard, and Dashy doesn't meet your needs, I highly recommend you check these projects out! Including, but not limited to: [HomeDash2](https://lamarios.github.io/Homedash2), [Homer](https://github.com/bastienwirtz/homer) (`Apache License 2.0`), [Organizr](https://organizr.app/) (`GPL-3.0 License`) and [Heimdall](https://github.com/linuxserver/Heimdall) (`MIT License`)

View File

@ -1,21 +0,0 @@
# Node.js with Vue
# Build a Node.js project that uses Vue.
# Add steps that analyze code, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript
trigger:
- master
pool:
vmImage: ubuntu-latest
steps:
- task: NodeTool@0
inputs:
versionSpec: '10.x'
displayName: 'Install Node.js'
- script: |
npm install
npm run build
displayName: 'npm install and build'

37
bin/healthcheck.js Normal file
View File

@ -0,0 +1,37 @@
/**
* An endpoint for confirming that the application is up and running
* Used for better Docker healthcheck results
* Note that exiting with code 1 indicates failure, and 0 is success
*/
const http = require('http');
/* Location of the server to test */
const port = process.env.PORT || !!process.env.IS_DOCKER ? 80 : 4000;
const host = process.env.HOST || '0.0.0.0';
const timeout = 2000;
const requestOptions = { host, port, timeout };
const startTime = new Date();
console.log(`[${startTime}] Running health check...`);
/* Starts quick HTTP server, attempts to send GET to app, then exists with appropriate exit code */
const healthCheck = http.request(requestOptions, (response) => {
const totalTime = (new Date() - startTime) / 1000;
const status = response.statusCode;
const color = status === 200 ? '\x1b[32m' : '\x1b[31m';
const message = `${color}Status: ${status}\nRequest took ${totalTime} seconds\n\x1b[0m---`;
console.log(message);
if (status == 200) { process.exit(0); }
else { process.exit(1); }
});
/* If the server is not running, then print the error code, and exit with 1 */
healthCheck.on('error', (err) => {
console.error(`\x1b[31mHealthceck Failed, Error: ${'\033[4m'}${err.code}\x1b[0m`);
process.exit(1);
});
healthCheck.end();

View File

@ -1,14 +1,25 @@
---
version: "3"
# Welcome to Dashy! To get started, run `docker compose up`
version: "3.8"
services:
dashy:
# To build from source, replace 'image: lissy93/dashy' with 'build: .'
# build: .
image: lissy93/dashy
container_name: dashy
volumes:
- /root/my-config.yml:/app/public/conf.yml
container_name: Dashy
# Pass in your config file below, by specifying the path on your host machine
# volumes:
# - /root/my-config.yml:/app/public/conf.yml
ports:
- 8080:80
environment:
- UID=1000
- GID=1000
- 4000:80
# Specify your user ID and group ID. You can find this by running `id -u` and `id -g`
# environment:
# - UID=1000
# - GID=1000
restart: unless-stopped
healthcheck:
test: ['CMD', 'node', '/app/bin/healthcheck']
interval: 1m30s
timeout: 10s
retries: 3
start_period: 40s

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 MiB

View File

@ -49,6 +49,7 @@ All fields are optional, unless otherwise stated.
**`cssThemes`** | `string[]` | _Optional_ | An array of custom theme names which can be used in the theme switcher dropdown
**`externalStyleSheet`** | `string` or `string[]` | _Optional_ | Either a URL to an external stylesheet or an array or URLs, which can be applied as themes within the UI
**`customCss`** | `string` | _Optional_ | Raw CSS that will be applied to the page. This can also be set from the UI. Please minify it first.
**`showSplashScreen`** | `boolean` | _Optional_ | Should display a splash screen while the app is loading. Defaults to false, except on first load
#### `section`

View File

@ -64,7 +64,12 @@ on how to create a pull request..
4. Make sure to update, or add to the tests when appropriate. Patches and
features will not be accepted without tests. Run `yarn test` to check that
all tests pass after you've made changes.
all tests pass after you've made changes, and `yarn lint` for linting.
```bash
git add ./path/to/modified/files
git commit -m "Fixed #xx by doing xyz"
```
5. If you added or changed a feature, make sure to document it accordingly in
the docs and if applicable, in the `README.md` file.
@ -97,6 +102,8 @@ Please also ensure that running the following scripts return no errors:
A good resource for testing the Docker image on a totally fresh system, is by using [Play with Docker](https://labs.play-with-docker.com/). This will let you clone or pull your image, and spin up a container. This is useful for checking that everything behaves as it should on an independent system, and should get around the _'works on my computer'_ issue.
All required checks will be run as a git-hook after doing a git commit. If you have any issues wit this, it can be disabled with the `--no-verify` flag
#### Merging a PR
Only maintainers can merge a PR. A pull request can only be merged if:

View File

@ -38,6 +38,11 @@ There is also:
- `yarn build-and-start` will run `yarn build` and `yarn start`
- `yarn build-watch` will output contents to `./dist` and recompile when anything in `./src` is modified, you can then use either `yarn start` or your own server, to have a production environment that watches for changes.
Using the Vue CLI:
- The app is build with Vue, and uses the [Vue-CLI Service](https://cli.vuejs.org/guide/cli-service.html) for basic commands.
- If you have [NPX](https://github.com/npm/npx) installed, then you can invoke the Vue CLI binary using `npx vue-cli-service [command]`
- Vue also has a GUI environment that can be used for basic project management, and may be useful for beginners, this can be started by running `vue ui`, and opening up `http://localhost:8000`
Note:
- If you are using NPM, replace `yarn` with `npm run`
- If you are using Docker, precede each command with `docker exec -it [container-id]`. Container ID can be found by running `docker ps`
@ -59,7 +64,7 @@ As well as Node, Git and Docker- you'll also need an IDE (e.g. [VS Code](https:/
### Style Guide
Linting is done using [ESLint](https://eslint.org/), and using the [Vue.js Styleguide](https://github.com/vuejs/eslint-config-standard), which is very similar to the [AirBnB Stylguide](https://github.com/airbnb/javascript). You can run `yarn lint` to report and fix issues. While the dev server is running, issues will be reported to the console automatically. Any lint errors will trigger the build to fail. Note that all lint checks must pass before any PR can be merged.
Linting is done using [ESLint](https://eslint.org/), and using the [Vue.js Styleguide](https://github.com/vuejs/eslint-config-standard), which is very similar to the [AirBnB Stylguide](https://github.com/airbnb/javascript). You can run `yarn lint` to report and fix issues. While the dev server is running, issues will be reported to the console automatically. Any lint errors will trigger the build to fail. Note that all lint checks must pass before any PR can be merged. Linting is also run as a git pre-commit hook
The most significant things to note are:
- Indentation should be done with two spaces
@ -108,6 +113,15 @@ Checklist:
- 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
#### 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.
### Development Tools
#### Performance - Lighthouse
The easiest method of checking performance is to use Chromium's build in auditing tool, Lighthouse. To run the test, open Developer Tools (usually F12) --> Lighthouse and click on the 'Generate Report' button at the bottom.
### Directory Structure
#### Files in the Root: `./`
@ -215,3 +229,16 @@ At it's core, the application uses [Vue.js](https://github.com/vuejs/vue), as we
- [`connect`](https://github.com/senchalabs/connect) - Minimilistic middleware layer for chaining together Node.js requests handled by the server file `MIT`
- [`serve-static`](https://github.com/expressjs/serve-static) - Lightweight static Node file server `MIT`
##### External Services
The 1-Click deploy demo uses [Play-with-Docker Labs](https://play-with-docker.com/). Code is hosted on [GitHub](https://github.com), Docker image is hosted on [DockerHub](https://hub.docker.com/), and the demos are hosted on [Netlify](https://www.netlify.com/).
### Notes
#### Known Warnings
When running the build command, several warnings appear. These are not errors, and do not affect the security or performance of the application. They will be addressed in a future update
`WARN A new version of sass-loader is available. Please upgrade for best experience.` - Currently we're using an older version of SASS loader, since the more recent releases do not seem to be compatible with the Vue CLI's webpack configuration.
`WARN asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).` - For the PWA to support Windows 10, a splash screen asset is required, and is quite large. This throws a warning, however PWA assets are not loaded until needed, so shouldn't have any impact on application performance. A similar warning is thrown for the Raleway font, and that is looking to be addressed.

View File

@ -1,11 +1,30 @@
## Getting Started
# Getting Started
- [Deployment](#deployment)
- [1-Click Deploy](#1-click-deploy)
- [Deploy with Docker](#deploy-with-docker)
- [Deploy from Source](#deploy-from-source)
- [Usage](#usage)
- [Providing Assets](#providing-assets)
- [Basic Commands](#basic-commands)
- [Updating](#updating)
- [Updating Docker Container](#updating-docker-container)
- [Automating Docker Updates](#automating-docker-updates)
- [Updating from Source](#updating-from-source)
## Deployment
### 1-Click Deploy
If you just want to test Dashy out, then you have several options:
- You can spin up a container with PWD by [clicking here](https://labs.play-with-docker.com/?stack=https://raw.githubusercontent.com/Lissy93/dashy/master/docker-compose.yml)
- Or on your own system, by running: `docker run -p 8080:80 lissy93/dashy`, then open your browser and visit `http://localhost:8080`
- Or you can check out the live demo, [here](http://dashy-demo-1.as93.net/)
### Deploy with Docker
The quickest way to get started on any system is with Docker, and Dashy is available though [Docker Hub](https://hub.docker.com/r/lissy93/dashy). You will need [Docker](https://docs.docker.com/get-docker/) installed on your system.
To test it out, just run: `docker run -p 8080:80 lissy93/dashy`, then open your browser and visit `http://localhost:8080`.
To configure Dashy with your own services, and customize it to your liking, you will need to write a config file, and pass it to the Docker container as a volume.
```docker
@ -27,6 +46,12 @@ Explanation of the above options:
For all available options, and to learn more, see the [Docker Run Docs](https://docs.docker.com/engine/reference/commandline/run/)
You can also build and deploy the Docker container from source.
- Get the code: `git clone git@github.com:Lissy93/dashy.git && cd dashy`
- Edit the `./public/conf.yml` file and take a look at the `docker-compose.yml`
- Start the container: `docker compose up`
### Deploy from Source
If you do not want to use Docker, you can run Dashy directly on your host system. For this, you will need both [git](https://git-scm.com/downloads) and the latest or LTS version of [Node.js](https://nodejs.org/) installed.
@ -36,6 +61,9 @@ If you do not want to use Docker, you can run Dashy directly on your host system
4. Build: `yarn build`
5. Run: `yarn start`
---
## Usage
### Providing Assets
Although not essential, you will most likely want to provide several assets to Dashy. All web assets can be found in the `/public` directory.
@ -43,14 +71,54 @@ Although not essential, you will most likely want to provide several assets to D
- `./public/item-icons` - If you're using your own icons, you can choose to store them locally for better load time, and this is the directory to put them in. You can also use sub-folders here to keep things organized. You then reference these assets relative this the direcroties path, for example: to use `./public/item-icons/networking/netdata.png` as an icon for one of your links, you would set `icon: networking/netdata.png`
- Also within `./public` you'll find standard website assets, including `favicon.ico`, `manifest.json`, `robots.txt`, etc. There's no need to modify these, but you can do so if you wish.
### Healthchecks
Healthchecks are configured to periodically check that Dashy is up and running correctly on the specified port. By default, the health script is called every 5 minutes, but this can be modified with the `--health-interval` option. You can check the current container health with: `docker inspect --format "{{json .State.Health }}" [container-id]`. You can also manually request the applications status by running `docker exec -it [container-id] yarn health-check`. You can disable healthchecks altogether by adding the `--no-healthcheck` flag to your Docker run command.
### Basic Commands
Now that you've got Dashy running, there are a few commands that you need to know.
The following commands are defined in the [`package.json`](https://github.com/Lissy93/dashy/blob/master/package.json#L5) file, and are run with `yarn`. If you prefer, you can use NPM, just replace instances of `yarn` with `npm run`. If you are using Docker, then you will need to precede each command with `docker exec -it [container-id]`, where container ID can be found by running `docker ps`. For example `docker exec -it 26c156c467b4 yarn build`
The following commands are defined in the [`package.json`](https://github.com/Lissy93/dashy/blob/master/package.json#L5) file, and are run with `yarn`. If you prefer, you can use NPM, just replace instances of `yarn` with `npm run`. If you are using Docker, then you will need to precede each command with `docker exec -it [container-id]`, where container ID can be found by running `docker ps`. For example `docker exec -it 26c156c467b4 yarn build`.
#### `yarn build`
In the interest of speed, the application is pre-compiled, this means that the config file is read during build-time, and therefore the app needs to rebuilt for any new changes to take effect. Luckily this is very straight forward. Just run `yarn build` or `docker exec -it [container-id] yarn build`.
- **`yarn build`** - In the interest of speed, the application is pre-compiled, this means that the config file is read during build-time, and therefore the app needs to rebuilt for any new changes to take effect. Luckily this is very straight forward. Just run `yarn build` or `docker exec -it [container-id] yarn build`
- **`yarn validate-config`** - If you have quite a long configuration file, you may wish to check that it's all good to go, before deploying the app. This can be done with `yarn validate-config` or `docker exec -it [container-id] yarn validate-config`. Your config file needs to be in `/public/conf.yml` (or within your Docker container at `/app/public/conf.yml`). This will first check that your YAML is valid, and then validates it against Dashy's [schema](https://github.com/Lissy93/dashy/blob/master/src/utils/ConfigSchema.js).
- **`yarn health-check`** - Checks that the application is up and running on it's specified port, and outputs current status and response times. Useful for integrating into your monitoring service, if you need to maintain high system availability
- **`yarn build-watch`** - If you find yourself making frequent changes to your configuration, and do not want to have to keep manually rebuilding, then this option is for you. It will watch for changes to any files within the projects root, and then trigger a rebuild. Note that if you are developing new features, then `yarn dev` would be more appropriate, as it's significantly faster at recompiling (under 1 second), and has hot reloading, linting and testing integrated
- **`yarn build-and-start`** - Builds the app, runs checks and starts the production server. Commands are run in parallel, and so is faster than running them in independently
#### `yarn validate-config`
If you have quite a long configuration file, you may wish to check that it's all good to go, before deploying the app. This can be done with `yarn validate-config` or `docker exec -it [container-id] yarn validate-config`. Your config file needs to be in `/public/conf.yml` (or within your Docker container at `/app/public/conf.yml`). This will first check that your YAML is valid, and then validates it against Dashy's [schema](https://github.com/Lissy93/dashy/blob/master/src/utils/ConfigSchema.js).
---
## Updating
Dashy is under active development, so to take advantage of the latest features, you may need to update your instance every now and again.
### Updating Docker Container
1. Pull latest image: `docker pull lissy93/dashy:latest`
2. Kill off existing container
- Find container ID: `docker ps`
- Stop container: `docker stop [container_id]`
- Remove container: `docker rm [container_id]`
3. Spin up new container: `docker run [params] lissy93/dashy`
### Automatic Docker Updates
You can automate the above process using [Watchtower](https://github.com/containrrr/watchtower).
Watchtower will watch for new versions of a given image on Docker Hub, pull down your new image, gracefully shut down your existing container and restart it with the same options that were used when it was deployed initially.
To get started, spin up the watchtower container:
```
docker run -d \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower
```
For more information, see the [Watchtower Docs](https://containrrr.dev/watchtower/)
### Updating Dashy from Source
1. Navigate into directory: `cd ./dashy`
2. Stop your current instance
3. Pull latest code: `git pull origin master`
4. Re-build: `yarn build`
5. Start: `yarn start`

View File

@ -9,7 +9,8 @@
"lint": "vue-cli-service lint --fix",
"build-watch": "vue-cli-service build --watch",
"build-and-start": "npm-run-all --parallel build start",
"validate-config": "node src/utils/ConfigValidator"
"validate-config": "node src/utils/ConfigValidator",
"health-check": "node bin/healthcheck"
},
"dependencies": {
"ajv": "^8.5.0",
@ -17,6 +18,7 @@
"connect": "^3.7.0",
"crypto-js": "^4.0.0",
"highlight.js": "^11.0.0",
"js-yaml": "^4.1.0",
"npm-run-all": "^4.1.5",
"prismjs": "^1.23.0",
"register-service-worker": "^1.6.2",
@ -48,6 +50,9 @@
"vue-svg-loader": "^0.16.0",
"vue-template-compiler": "^2.6.10"
},
"gitHooks": {
"pre-commit": "yarn lint"
},
"eslintConfig": {
"root": true,
"env": {

7
public/item-icons/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
# Place any custom icons used by your instance of Dashy here.
# For more info, see Icon docs at: https://git.io/JZwc5
# Ignore everything in this directory
*
# Except this file
!.gitignore

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,3 +1,5 @@
/* eslint-disable no-console */
/* This is a simple Node.js http server, that is used to serve up the contents of ./dist */
const connect = require('connect');
const serveStatic = require('serve-static');
@ -7,70 +9,65 @@ const os = require('os');
require('./src/utils/ConfigValidator');
const port = process.env.PORT || 80;
const isDocker = !!process.env.IS_DOCKER;
/* eslint no-console: 0 */
const printWelcomeMessage = () => {
getLocalIp().then(({ address }) => {
const ip = address || 'localhost';
console.log(overComplicatedMessage(ip, port));
});
}
/* Checks env var for port. If undefined, will use Port 80 for Docker, or 4000 for metal */
const port = process.env.PORT || isDocker ? 80 : 4000;
const getLocalIp = () => {
const dnsLookup = util.promisify(dns.lookup);
return dnsLookup(os.hostname());
}
};
const overComplicatedMessage = (ip, port) => {
const overComplicatedMessage = (ip) => {
let msg = '';
const chars = {
RESET: '\x1b[0m',
CYAN: '\x1b[36m',
GREEN: '\x1b[32m',
BLUE: '\x1b[34m',
UNDERLINE: '\033[4m',
BOLD: '\033[1m',
BRIGHT: '\x1b[1m',
BR: '\n',
};
const stars = (count) => new Array(count).fill('*').join('');
const line = (count) => new Array(count).fill('━').join('');
const blanks = (count) => new Array(count).fill(' ').join('');
if (process.env.IS_DOCKER) {
if (isDocker) {
const containerId = process.env.HOSTNAME || undefined;
msg = `${chars.BLUE}${stars(91)}${chars.BR}${chars.RESET}`
+ `${chars.CYAN}${chars.BOLD}Welcome to Dashy! 🚀${chars.RESET}${chars.BR}`
+ `${chars.CYAN}Welcome to Dashy! 🚀${chars.RESET}${chars.BR}`
+ `${chars.GREEN}Your new dashboard is now up and running `
+ `${containerId ? `in container ID ${containerId}` : 'with Docker'}${chars.BR}`
+ `${chars.GREEN}After updating your config file, run `
+ `'${chars.UNDERLINE}docker exec -it ${containerId || '[container-id]'} yarn build`
+ `'${chars.BRIGHT}docker exec -it ${containerId || '[container-id]'} yarn build`
+ `${chars.RESET}${chars.GREEN}' to rebuild${chars.BR}`
+ `${chars.BLUE}${stars(91)}${chars.BR}${chars.RESET}`;
} else {
msg = `${chars.GREEN}${line(75)}${chars.BR}`
+ `${chars.CYAN}${chars.BOLD}Welcome to Dashy! 🚀${blanks(55)}${chars.GREEN}${chars.BR}`
+ `${chars.CYAN}Your new dashboard is now up and running at ${chars.UNDERLINE}`
+ `http://${ip}:${port}${chars.RESET}${blanks(20 - ip.length)}${chars.GREEN}${chars.BR}`
+ `${chars.CYAN}After updating your config file, run '${chars.UNDERLINE}yarn build`
+ `${chars.CYAN}Welcome to Dashy! 🚀${blanks(55)}${chars.GREEN}${chars.BR}`
+ `${chars.CYAN}Your new dashboard is now up and running at ${chars.BRIGHT}`
+ `http://${ip}:${port}${chars.RESET}${blanks(18 - ip.length)}${chars.GREEN}${chars.BR}`
+ `${chars.CYAN}After updating your config file, run '${chars.BRIGHT}yarn build`
+ `${chars.RESET}${chars.CYAN}' to rebuild the app${blanks(6)}${chars.GREEN}${chars.BR}`
+ `${line(75)}${chars.BR}${chars.BR}`;
}
return msg;
}
};
function send404(req, res) {
// send your 404 here
res.statusCode = 404
res.end('nothing here!')
}
/* eslint no-console: 0 */
const printWelcomeMessage = () => {
getLocalIp().then(({ address }) => {
const ip = address || 'localhost';
console.log(overComplicatedMessage(ip));
});
};
try {
connect()
.use(serveStatic(`${__dirname}/dist`))
.use(serveStatic(`${__dirname}/public`, { index: 'default.html' }))
.listen(port, () => {
try { printWelcomeMessage(port); }
catch (e) { console.log('Dashy is Starting...'); }
try { printWelcomeMessage(); } catch (e) { console.log('Dashy is Starting...'); }
});
} catch (error) {
console.log('Sorry, an error occurred ', error);

View File

@ -1,5 +1,6 @@
<template>
<div id="dashy" data-theme="dark">
<div id="dashy">
<LoadingScreen :isLoading="isLoading" v-if="shouldShowSplash()" />
<Header :pageInfo="pageInfo" />
<router-view />
<Footer v-if="showFooter" :text="getFooterText()" />
@ -9,7 +10,8 @@
import Header from '@/components/PageStrcture/Header.vue';
import Footer from '@/components/PageStrcture/Footer.vue';
import Defaults, { localStorageKeys } from '@/utils/defaults';
import LoadingScreen from '@/components/PageStrcture/LoadingScreen.vue';
import Defaults, { localStorageKeys, splashScreenTime } from '@/utils/defaults';
import conf from '../public/conf.yml';
export default {
@ -17,11 +19,15 @@ export default {
components: {
Header,
Footer,
LoadingScreen,
},
data() {
return {
// pageInfo: this.getPageInfo(conf.pageInfo),
showFooter: Defaults.visibleComponents.footer,
isLoading: true,
};
},
data: () => ({
// pageInfo: this.getPageInfo(conf.pageInfo),
showFooter: Defaults.visibleComponents.footer,
}),
computed: {
pageInfo() {
return this.getPageInfo(conf.pageInfo);
@ -68,8 +74,19 @@ export default {
style.textContent = usersCss;
document.head.append(style);
},
shouldShowSplash() {
return this.appConfig.showSplashScreen || !localStorage[localStorageKeys.HIDE_WELCOME_BANNER];
},
hideSplash() {
if (this.shouldShowSplash()) {
setTimeout(() => { this.isLoading = false; }, splashScreenTime || 2000);
} else {
this.isLoading = false;
}
},
},
mounted() {
this.hideSplash();
if (this.appConfig.customCss) {
const cleanedCss = this.appConfig.customCss.replace(/<\/?[^>]+(>|$)/g, '');
this.injectCustomStyles(cleanedCss);

View File

@ -0,0 +1,114 @@
<template>
<transition name="slide-fade">
<div id="loading" v-if="isLoading" :class="c" @click="c = 'hide'">
<h2>Dashy</h2>
<div class="inner-container">
<p>Loading</p>
<span class="dots-cont">
<span class="dot dot-1"></span>
<span class="dot dot-2"></span>
<span class="dot dot-3"></span>
<span class="dot dot-4"></span>
</span>
</div>
</div>
</transition>
</template>
<script>
export default {
name: 'LoadingScreen',
props: {
isLoading: { type: Boolean, default: false },
},
data: () => ({
c: '',
}),
};
</script>
<style scoped lang="scss">
div#loading {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
position: absolute;
height: 100%;
width: 100%;
z-index: 7;
background: var(--loading-screen-background);
color: var(--loading-screen-color);
&.hide { display: none; }
.inner-container {
text-align: center;
}
p {
font-size: 6vw;
display: inline;
margin: 0 auto;
}
h2 {
opacity: 0.35;
font-size: 16vw;
margin: 0;
}
.dots-cont {
display: inline;
.dot {
width: 4px;
height: 4px;
background: var(--loading-screen-color);
display: inline-block;
border-radius: 35%;
right: 0px;
bottom: 0px;
margin: 0px 2.5px;
position: relative;
animation: jump 1s infinite;
&.dot-1 {
-webkit-animation-delay: 100ms;
animation-delay: 100ms;
}
&.dot-2 {
-webkit-animation-delay: 200ms;
animation-delay: 200ms;
}
&.dot-3 {
-webkit-animation-delay: 300ms;
animation-delay: 300ms;
}
&.dot-4 {
-webkit-animation-delay: 400ms;
animation-delay: 400ms;
}
}
}
@keyframes jump {
0% {
bottom: 0px;
}
20% {
bottom: 5px;
}
40% {
bottom: 0px;
}
}
}
.slide-fade-leave-active {
transition: all .2s cubic-bezier(1, 0.9, 0.7, 0.4);
}
.slide-fade-enter, .slide-fade-leave-to {
transform: translateY(-200px);
opacity: 0;
}
</style>

View File

@ -75,7 +75,7 @@ export default {
right: 10px;
margin: 0.5em;
padding: 0.1em 0.3em;
z-index: 10;
z-index: 6;
border-radius: 12px;
border: 1px solid var(--welcome-popup-background);
-webkit-box-shadow: 2px 1px 5px #130f23;

View File

@ -77,4 +77,7 @@
--toast-color: var(--background);
--scroll-bar-color: var(--primary);
--scroll-bar-background: var(--background-darker);
--loading-screen-color: var(--primary);
--loading-screen-background: var(--background);
}

View File

@ -431,11 +431,13 @@ html[data-theme='material'] {
--settings-text-color: #363636;
--config-code-color: #363636;
--config-settings-background: #fff;
--config-settings-color: #363636;
--config-settings-color: #473f3f;
--heading-text-color: #fff;
--curve-factor: 4px;
--curve-factor-navbar: 8px;
--search-container-background: #4285f4;
--welcome-popup-text-color: #f5f5f5;
--footer-text-color: #f5f5f5cc;
header {
background: #4285f4;

View File

@ -1,33 +1,33 @@
{
"type": "object",
"required": [
"sections"
],
"additionalProperties": false,
"properties": {
"required": [
"sections"
],
"additionalProperties": false,
"properties": {
"pageInfo": {
"type": "object",
"properties": {
"properties": {
"title": {
"type": "string",
"description": "Title and heading for the app"
"description": "Title and heading for the app"
},
"description": {
"type": "string",
"description": "Sub-title, displayed in header"
"description": "Sub-title, displayed in header"
},
"navLinks": {
"type": "array",
"maxItems": 6,
"description": "Quick access links, displayed in header",
"items": {
"maxItems": 6,
"description": "Quick access links, displayed in header",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"title",
"path"
],
"properties": {
"additionalProperties": false,
"required": [
"title",
"path"
],
"properties": {
"title": {
"type": "string"
},
@ -44,91 +44,96 @@
"required": [
"title"
],
"additionalProperties": false
"additionalProperties": false
},
"appConfig": {
"type": "object",
"description": "Application configuration",
"properties": {
"description": "Application configuration",
"properties": {
"backgroundImg": {
"type": "string",
"description": "A URL to an image asset to be displayed as background"
"description": "A URL to an image asset to be displayed as background"
},
"theme": {
"type": "string",
"default": "Callisto",
"description": "A theme to be applied by default on first load"
"default": "Callisto",
"description": "A theme to be applied by default on first load"
},
"enableFontAwesome": {
"type": "boolean",
"default": true,
"description": "Should load font-awesome assets"
"default": true,
"description": "Should load font-awesome assets"
},
"fontAwesomeKey": {
"type": "string",
"pattern": "^[a-z0-9]{10}$",
"description": "API key for font-awesome"
"pattern": "^[a-z0-9]{10}$",
"description": "API key for font-awesome"
},
"cssThemes": {
"type": "array",
"description": "Theme names to be added to the dropdown",
"items": {
"description": "Theme names to be added to the dropdown",
"items": {
"type": "string"
}
},
"externalStyleSheet": {
"description": "URL or URLs of external stylesheets to add to dropdown/ load",
"type": [
"string",
"array"
],
"items": {
"type": [
"string",
"array"
],
"items": {
"type": "string"
}
},
"customCss": {
"type": "string",
"description": "Any custom CSS overides, must be minified"
"description": "Any custom CSS overides, must be minified"
},
"showSplashScreen": {
"type": "boolean",
"default": false,
"description": "Display a loading screen when the app is launched"
}
},
"additionalProperties": false
},
"sections": {
"type": "array",
"description": "Array of sections, containing items",
"items": {
"description": "Array of sections, containing items",
"items": {
"type": "object",
"required": [
"name",
"items"
],
"additionalProperties": false,
"properties": {
"required": [
"name",
"items"
],
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "Title/ heading for a section"
"description": "Title/ heading for a section"
},
"icon": {
"type": "string",
"description": "Icon will be displayed next to title"
"description": "Icon will be displayed next to title"
},
"displayData": {
"type": "object",
"additionalProperties": false,
"description": "Optional meta data for customizing a section",
"properties": {
"additionalProperties": false,
"description": "Optional meta data for customizing a section",
"properties": {
"collapsed": {
"type": "boolean",
"default": false,
"description": "If true, section needs to be clicked to open"
"default": false,
"description": "If true, section needs to be clicked to open"
},
"color": {
"type": "string",
"description": "Hex code, or HTML color for section fill"
"description": "Hex code, or HTML color for section fill"
},
"customStyles": {
"type": "string",
"description": "CSS overides for section container"
"description": "CSS overides for section container"
},
"itemSize": {
"enum": [
@ -136,72 +141,72 @@
"medium",
"large"
],
"default": "medium",
"description": "Size of items within the section"
"default": "medium",
"description": "Size of items within the section"
},
"rows": {
"type": "number",
"minimum": 1,
"maximum": 5,
"default": 1,
"description": "The amount of space that the section spans vertically"
"minimum": 1,
"maximum": 5,
"default": 1,
"description": "The amount of space that the section spans vertically"
},
"cols": {
"type": "number",
"minimum": 1,
"maximum": 5,
"default": 1,
"description": "The amount of space that the section spans horizontally"
"minimum": 1,
"maximum": 5,
"default": 1,
"description": "The amount of space that the section spans horizontally"
},
"layout": {
"enum": [
"grid",
"auto"
],
"default": "auto",
"description": "If set to grid, items have uniform width, and itemCount can be set"
"default": "auto",
"description": "If set to grid, items have uniform width, and itemCount can be set"
},
"itemCountX": {
"type": "number",
"minimum": 1,
"maximum": 12,
"description": "Number of items per column"
"minimum": 1,
"maximum": 12,
"description": "Number of items per column"
},
"itemCountY": {
"type": "number",
"minimum": 1,
"maximum": 12,
"description": "Number of items per row"
"minimum": 1,
"maximum": 12,
"description": "Number of items per row"
}
}
},
"items": {
"type": "array",
"description": "Array of items to display with a section",
"items": {
"description": "Array of items to display with a section",
"items": {
"type": "object",
"additionalProperties": false,
"required": [
"title"
],
"properties": {
"additionalProperties": false,
"required": [
"title"
],
"properties": {
"title": {
"type": "string",
"description": "Text shown on the item"
"description": "Text shown on the item"
},
"description": {
"type": "string",
"nullable": true,
"description": "Short description, shown on hover or in a tooltip"
"nullable": true,
"description": "Short description, shown on hover or in a tooltip"
},
"icon": {
"type": "string",
"nullable": true,
"description": "An icon, either as a font-awesome identifier, local or remote URL, or auto-fetched favicon"
"nullable": true,
"description": "An icon, either as a font-awesome identifier, local or remote URL, or auto-fetched favicon"
},
"url": {
"type": "string",
"description": "The destination to navigate to when item is clicked"
"description": "The destination to navigate to when item is clicked"
},
"target": {
"enum": [
@ -209,16 +214,16 @@
"sametab",
"iframe"
],
"default": "newtab",
"description": "Opening method, when item is clicked"
"default": "newtab",
"description": "Opening method, when item is clicked"
},
"color": {
"type": "string",
"description": "A custom fill color of the item"
"description": "A custom fill color of the item"
},
"provider": {
"type": "string",
"description": "Provider name, e.g. Microsoft"
"description": "Provider name, e.g. Microsoft"
}
}
}

View File

@ -1,3 +1,5 @@
/* 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');
@ -13,33 +15,31 @@ const validatorOptions = {
const ajv = new Ajv(validatorOptions);
/* Message printed when validation was successful */
const successMsg = () => {
return '\x1b[1m\x1b[32m\033[1mNo issues found, your configuration is valid :)\x1b[0m\n';
}
const successMsg = () => '\x1b[1m\x1b[32mNo issues found, your configuration is valid :)\x1b[0m\n';
/* Formats error message. ready for printing to the console */
const errorMsg = (output) => {
const warningFont = '\033[1m\x1b[103m\x1b[34m';
const warningFont = '\x1b[103m\x1b[34m';
const line = `${warningFont}${new Array(42).fill('━').join('')}\x1b[0m`;
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 += `${'\033[1m\x1b[36m'}${index + 1}. ${details.keyword} ${details.message} `
+ `in ${'\033[4m'}${details.instancePath}\x1b[0m\n`;
msg += `${'\x1b[36m'}${index + 1}. ${details.keyword} ${details.message} `
+ `in ${details.instancePath}\x1b[0m\n`;
});
return msg;
};
/* Error message printed when the file could not be opened */
const bigError = () => {
const formatting = '\033[31m\033[1m\033[47m';
const line = `${formatting}${new Array(41).fill('━').join('')}\x1b[0m\n`;
const msg = `${formatting} Error, unable to find / open 'conf.yml' \x1b[0m\n`;
return `${line}${msg}${line}\n`;
}
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`;
};
/* Start the validation */
const validate = (config, schema) => {
const validate = (config) => {
console.log('\nChecking config file against schema...');
const valid = ajv.validate(schema, config);
if (valid) {
@ -47,12 +47,17 @@ const validate = (config, schema) => {
} else {
console.log(errorMsg(ajv.errors));
}
}
};
try {
const config = yaml.safeLoad(fs.readFileSync('./public/conf.yml', 'utf8'));
validate(config, schema);
const config = yaml.load(fs.readFileSync('./public/conf.yml', 'utf8'));
validate(config);
} catch (e) {
console.log(bigError(), e);
console.log(bigError());
console.log('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 ConfigValidators.js:\x1b[0m\n', e);
console.log('\n\n');
}

View File

@ -72,4 +72,5 @@ module.exports = {
iconPack: 'fontawesome',
},
backupEndpoint: 'https://dashy-sync-service.as93.net',
splashScreenTime: 1900,
};

1309
yarn.lock

File diff suppressed because it is too large Load Diff