🔀 Merge pull request #201 from Lissy93/REFACTOR/minor-improvments

[BUG-FIXES] - Minor UI bug fixes and build refactor
Fixed #196 #198 #199
This commit is contained in:
Alicia Sykes 2021-09-04 21:34:07 +01:00 committed by GitHub
commit 09bc8a32f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 220 additions and 78 deletions

31
.env
View File

@ -1,9 +1,26 @@
# Store environmental variables here. All variables are optional.
# Lines beginning in '#' are ignored.
# NODE_ENV=production # Can be either development, production or test
# PORT=4000 # The port to expose the running application on
# HOST=localhost # The host that Dashy is running on, domain or IP
# BASE_URL=./ # The default base path for serving up static assets
# VUE_APP_DOMAIN # Usually the same as BASE_URL, but accessible in frontend
# IS_DOCKER=true # Usually already set, should be true if running in container
# VUE_APP_VERSION # Again, set automatically using package.json during build
# Can be either development, production or test
# NODE_ENV=production
# The port to expose the running application on
# PORT=4000
# The host that Dashy is running on, domain or IP
# HOST=localhost
# The default base path for serving up static assets
# BASE_URL=./
# Usually the same as BASE_URL, but accessible in frontend
# VUE_APP_DOMAIN=https://dashy.to
# Should enable SRI for build script and link resources
# INTEGRITY=true
# Computed automatically on build. Indicates if running in container
# IS_DOCKER=true
# Again, set automatically using package.json during build time
# VUE_APP_VERSION=1.7.0

10
.github/CHANGELOG.md vendored
View File

@ -1,5 +1,15 @@
# Changelog
## 🐛 1.7.1 - Lots of Tiny Fixes and Improvements [PR #200](https://github.com/Lissy93/dashy/pull/201)
- Removes background in console art
- Updates auto environmental variables
- Icon image assets max height Force same Icon/Item Height #200
- Adds an action to close spammy issues
- Adds option to enable SRI integrity, plus refactos PWA into defaults
- Updates privacy and security docs
- Adds option for different favicon API for each app [FEATURE_REQUEST] Allow using different faviconApi for each items #196
- Fixes loading of local SVG icons #199
## 🍻 1.7.0 - Documentation Website [PR #190](https://github.com/Lissy93/dashy/pull/190)
- Builds a quick website to host the docs. No code changes, but prepares for V1.7 release

6
.github/SECURITY.md vendored
View File

@ -27,3 +27,9 @@ Please use only English.
## Issues That Should Not Be Raised
Please do not raise issues in this repo which relate to Vue or Vue CLI, we're already using the latest versions of these dependencies, so any issues here to be taken up with Vue. The same applies to other dev dependencies that are at the latest version.
## Known Issues
> **01/09/2021** - [Inefficient Regular Expression Complexity](https://www.huntr.dev/bounties/1e8f07fc-c384-4ff9-8498-0690de2e8c31/) in Axios (Re: [CWE-1333](https://cwe.mitre.org/data/definitions/1333.html)).
This ReDos vuln, was raised and fixed by @ready-research in Axios in August 2021. The issue was resolved in [`5b45711`](https://github.com/axios/axios/commit/5b457116e31db0e88fede6c428e969e87f290929), but Snyk sometime just takes a while to show updates. Dashy is using the latest version of Axios, and so is not affected by this issue.

View File

@ -0,0 +1,27 @@
on:
issues:
types: [opened, reopened]
jobs:
greet:
runs-on: ubuntu-latest
name: Close issue opened by non-stargazer
steps:
- name: close
uses: uhyo/please-star-first@v1
with:
token: ${{ secrets.BOT_GITHUB_TOKEN }}
message: |
Welcome to Dashy 👋
It's great to have you here, but unfortunately your ticket has been closed to prevent spam and low quality issues. Please ensure the following criteria are met, before reopening this issue.
Issues are sometimes closed when users:
- Have only recently joined GitHub
- Have not yet stared this repository
- Have not previously interacted with the repo
Before you reopen this issue, please also ensure that:
- You have checked that a similar issue does not already exist
- You have checked the documentation for an existing solution
- You have completed the relevant sections in the Issue template
Once you have verified the above standards are met, you may reopen this issue.

View File

@ -1,13 +1,15 @@
## Icons
# Icons
Both sections and items can have an icon, which is specified using the `icon` attribute. Using icons improves the aesthetics of your UI and makes the app more intuitive to use. There are several options when it comes to setting icons, and this article outlines each of them
- [Font Awesome Icons](#font-awesome)
- [Simple Icons](#simple-icons)
- [Auto-Fetched Favicons](#favicons)
- [Generative Icons](#generative-icons)
- [Emoji Icons](#emoji-icons)
- [Icons by URL](#icons-by-url)
- [Local Icons](#local-icons)
- [Material Design Icons](#material-design-icons)
- [No Icon](#no-icon)
<p align="center">
@ -16,7 +18,9 @@ Both sections and items can have an icon, which is specified using the `icon` at
Note that, if you are using icons from an external source (like font-awesome or material-design-icons), then the relevant font file will be loaded in automatically if and when needed, but combining icons from multiple services may have a negative impact on performance.
### Font Awesome
---
## Font Awesome
You can use any [Font Awesome Icon](https://fontawesome.com/icons) simply by specifying it's identifier. This is in the format of `[category] [name]` and can be found on the page for any given icon on the Font Awesome site. For example: `fas fa-rocket`, `fab fa-monero` or `fas fa-unicorn`.
Font-Awesome has a wide variety of free icons, but you can also use their pro icons if you have a membership. To do so, you need to specify your license key under: `appConfig.fontAwesomeKey`. This is usually a 10-digit string, for example `13014ae648`.
@ -25,7 +29,29 @@ Font-Awesome has a wide variety of free icons, but you can also use their pro ic
<img width="580" src="https://i.ibb.co/pdrw8J4/fontawesome-icons2.png" />
</p>
### Favicons
---
## Simple Icons
[SimpleIcons.org](https://simpleicons.org/) is a collection of 2000+ high quality, free and open source brand and logo SVG icons. Usage of which is very similar to font-awesome icons. First find the glyph you want to use on the [website](https://simpleicons.org/), then just set your icon the the simple icon slug, prefixed with `si-`.
For example:
```yaml
sections:
- name: Simple Icons Example
items:
- title: Portainer
icon: si-portainer
- title: FreeNAS
icon: si-freenas
- title: NextCloud
icon: si-nextcloud
- title: Home Assistant
icon: si-homeassistant
```
---
## Favicons
Dashy can auto-fetch the favicon for a given service using it's URL. Just set `icon: favicon` to use this feature. If the services URL is a local IP, then Dashy will attempt to find the favicon from `http://[ip]/favicon.ico`. This has two issues, favicons are not always hosted at the same location for every service, and often the default favicon is a low resolution. Therefore to fix this, for remote services an API is used to return a high-quality icon for any online service.
<p align="center">
@ -42,41 +68,45 @@ The default favicon API is [Favicon Kit](https://faviconkit.com/), a free and re
You can also force Dashy to always get favicons from the root of the domain, and not use an external service, by setting `appConfig.faviconApi` to `local`.
### Generative Icons
To use a different favicon API for certain items, then set `icon: favicon-[api]`, e.g. `favicon-clearbit`
If for a given service none of the APIs work in your situation, and nor does local, then the best option is to find the path of the services logo or favicon, and set the icon to the URL of the raw image.
---
## Generative Icons
Uses a unique and programmatically generated icon for a given service. This is particularly useful when you have a lot of similar services with a different IP or port, and no specific icon. These icons are generated with [ipsicon.io](https://ipsicon.io/). To use this option, just set an item's to: `icon: generative`.
<p align="center">
<img width="400" src="https://i.ibb.co/qrNNNcm/generative-icons.png" />
</p>
### Emoji Icons
---
## Emoji Icons
You can use almost any emoji as an icon for items or sections. You can specify the emoji either by pasting it directly, using it's unicode ( e.g. `'U+1F680'`) or shortcode (e.g. `':rocket:'`). You can find these codes for any emoji using [Emojipedia](https://emojipedia.org/) (near the bottom of emoji each page), or for a quick reference to emoji shortcodes, check out [emojis.ninja](https://emojis.ninja/) by @nomanoff.
<p align="center">
<img width="580" src="https://i.ibb.co/YLwgTf9/emoji-icons-1.png" />
</p>
The following examples will all render the same rocket (🚀) emoji:
For example, these will all render the same rocket (🚀) emoji: `icon: ':rocket:'` or `icon: 'U+1F680'` or `icon: 🚀`
```yaml
items:
- title: Shortcode
icon: ':rocket:'
- title: Unicode
icon: 'U+1F680'
- title: Emoji
icon: 🚀
```
---
### Icons by URL
## Icons by URL
You can also set an icon by passing in a valid URL pointing to the icons location. For example `icon: https://i.ibb.co/710B3Yc/space-invader-x256.png`, this can be in .png, .jpg or .svg format, and hosted anywhere- so long as it's accessible from where you are hosting Dashy. The icon will be automatically scaled to fit, however loading in a lot of large icons may have a negative impact on performance, especially if you visit Dashy from new devices often.
### Local Icons
---
## Local Icons
You may also want to store your icons locally, bundled within Dashy so that there is no reliance on outside services. This can be done by putting the icons within Dashy's `./public/item-icons/` directory. If you are using Docker, then the easiest option is to map a volume from your host system, for example: `-v /local/image/directory:/app/public/item-icons/`. To reference an icon stored locally, just specify it's name and extension. For example, if my icon was stored in `/app/public/item-icons/maltrail.png`, then I would just set `icon: maltrail.png`.
You can also use sub-folders within the `item-icons` directory to keep things organised. You would then specify an icon with it's folder name slash image name. For example: `networking/monit.png`
### Material Design Icons
---
## Material Design Icons
Dashy also supports 5000+ [material-design-icons](https://github.com/Templarian/MaterialDesign). To use these, first find the name/ slug for your icon [here](https://dev.materialdesignicons.com/icons), and then prefix is with `mdi-`.
For example:
@ -93,8 +123,7 @@ sections:
```
### Simple Icons
To use glyphs from [SimpleIcons.org](https://simpleicons.org/), first find the icon slug, and then prefix it with `si-`. The image will be loaded directly from the Simple Icons
---
### No Icon
## No Icon
If you don't wish for a given item or section to have an icon, just leave out the `icon` attribute.

View File

@ -95,6 +95,18 @@ This is covered in more detail in [App Management](/docs/management.md).
---
## Security Features
#### Subresource Integrity
[Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) or SRI is a security feature that enables browsers to verify that resources they fetch are delivered without unexpected manipulation. It works by allowing you to provide a cryptographic hash that a fetched resource must match. This prevents the app from loading any resources that have been manipulated, by verifying the files hashes. It safeguards against the risk of an attacker injecting arbitrary malicious content into any files served up via a CDN.
Dashy supports SRI, and it is recommended to enable this if you are hosting your dashboard via a public CDN. To enable SRI, set the `INTEGRITY` environmental variable to `true`.
#### Authentication
Dashy supports both basic auth, as well as server-based SSO using Keycloak. Full details of which, along with alternate authentication methods can be found in the [Authentication Docs](/docs/authentication.md). If your dashboard is exposed to the internet and/ or contains any sensitive info it is strongly recommended to configure access control with Keycloak or another server-side method.
---
## Reporting a Security Issue
If you think you've found a critical issue with Dashy, please send an email to `security@mail.alicia.omg.lol`. You can encrypt it, using [`0688 F8D3 4587 D954 E9E5 1FB8 FEDB 68F5 5C02 83A7`](https://keybase.io/aliciasykes/pgp_keys.asc?fingerprint=0688f8d34587d954e9e51fb8fedb68f55c0283a7). You should receive a response within 48 hours.

View File

@ -1,6 +1,6 @@
{
"name": "Dashy",
"version": "1.7.0",
"version": "1.7.1",
"license": "MIT",
"main": "server",
"scripts": {
@ -92,4 +92,4 @@
"> 1%",
"last 2 versions"
]
}
}

View File

@ -43,7 +43,7 @@ module.exports = (ip, port, isDocker) => {
+ `${line(75)}${chars.BR}${chars.BR}${chars.RESET}`;
}
// Make some sexy ascii art ;)
const ascii = `\x1b[40m${chars.CYAN}\n\n`
const ascii = `${chars.CYAN}\n\n`
+ ' ██████╗ █████╗ ███████╗██╗ ██╗██╗ ██╗\n'
+ ' ██╔══██╗██╔══██╗██╔════╝██║ ██║╚██╗ ██╔╝\n'
+ ' ██║ ██║███████║███████╗███████║ ╚████╔╝\n'

View File

@ -62,7 +62,7 @@ export default {
/* Returns true if the input is a path to an image file */
isImage(img) {
const fileExtRegex = /(?:\.([^.]+))?$/;
const validImgExtensions = ['png', 'jpg'];
const validImgExtensions = ['svg', 'png', 'jpg'];
const splitPath = fileExtRegex.exec(img);
if (splitPath.length >= 1) return validImgExtensions.includes(splitPath[1]);
return false;
@ -89,18 +89,32 @@ export default {
return emojiCode; // Emoji is a glyph already, just return
},
/* Get favicon URL, for items which use the favicon as their icon */
getFavicon(fullUrl) {
getFavicon(fullUrl, specificApi) {
if (this.shouldUseDefaultFavicon(fullUrl)) { // Check if we should use local icon
const urlParts = fullUrl.split('/');
if (urlParts.length >= 2) return `${urlParts[0]}/${urlParts[1]}/${urlParts[2]}/${iconCdns.faviconName}`;
} else if (fullUrl.includes('http')) { // Service is running publicly
const host = this.getHostName(fullUrl);
const faviconApi = this.config.appConfig.faviconApi || defaultFaviconApi;
const faviconApi = specificApi || this.config.appConfig.faviconApi || defaultFaviconApi;
const endpoint = faviconApiEndpoints[faviconApi];
return endpoint.replace('$URL', host);
}
return '';
},
/* Get the URL for a favicon, but using the non-default favicon API */
getCustomFavicon(fullUrl, faviconIdentifier) {
const faviconApi = faviconIdentifier.split('favicon-')[1];
if (!faviconApi) {
ErrorHandler('Favicon API not specified');
} else if (!Object.keys(faviconApiEndpoints).includes(faviconApi)) {
ErrorHandler(`The specified favicon API, '${faviconApi}' cannot be found.`);
} else {
return this.getFavicon(fullUrl, faviconApi);
}
// Error encountered, favicon service not found
this.broken = true;
return undefined;
},
/* If using favicon for icon, and if service is running locally (determined by local IP) */
/* or if user prefers local favicon, then return true */
shouldUseDefaultFavicon(fullUrl) {
@ -127,6 +141,7 @@ export default {
case 'url': return img;
case 'img': return this.getLocalImagePath(img);
case 'favicon': return this.getFavicon(url);
case 'custom-favicon': return this.getCustomFavicon(url, img);
case 'generative': return this.getGenerativeIcon(url);
case 'mdi': return img; // Material design icons
case 'simple-icons': return this.getSimpleIcon(img);
@ -139,12 +154,12 @@ export default {
determineImageType(img) {
let imgType = '';
if (!img) imgType = 'none';
else if (img.endsWith('.svg')) imgType = 'svg';
else if (this.isUrl(img)) imgType = 'url';
else if (this.isImage(img)) imgType = 'img';
else if (img.includes('fa-')) imgType = 'font-awesome';
else if (img.includes('mdi-')) imgType = 'mdi';
else if (img.includes('si-')) imgType = 'si';
else if (img.includes('favicon-')) imgType = 'custom-favicon';
else if (img === 'favicon') imgType = 'favicon';
else if (img === 'generative') imgType = 'generative';
else if (this.isEmoji(img).isEmoji) imgType = 'emoji';
@ -167,15 +182,23 @@ export default {
<style lang="scss">
/* Default Image Icon */
.tile-icon {
width: 2rem;
// filter: var(--item-icon-transform);
min-width: 1rem;
max-width: 2rem;
min-height: 1rem;
max-height: 2rem;
object-fit: cover;
filter: var(--item-icon-transform);
border-radius: var(--curve-factor);
&.broken { display: none; }
&.small {
width: 1.5rem;
max-width: 1.5rem;
max-height: 1.5rem;
}
&.large {
width: 3rem;
max-width: 3rem;
max-height: 3rem;
}
&.broken {
display: none;
}
}
/* Font-Awesome and Material Design Icons */

View File

@ -16,7 +16,7 @@
--item-group-padding: 5px; // Determines width of item-group outline
--item-shadow: 1px 1px 2px #130f23;
--item-hover-shadow: 1px 2px 4px #373737;
--item-icon-transform: drop-shadow(2px 4px 6px var(--transparent-50)) saturate(0.65);
--item-icon-transform: drop-shadow(2px 4px 6px var(--transparent-50)) saturate(0.95);
--item-icon-transform-hover: drop-shadow(4px 8px 3px var(--transparent-50)) saturate(2);
--item-group-shadow: var(--item-shadow);
--context-menu-shadow: var(--item-shadow);

View File

@ -200,4 +200,19 @@ module.exports = {
guestAccess: 2,
notLoggedIn: 3,
},
/* Progressive Web App settings, used by Vue Config */
pwa: {
name: 'Dashy',
manifestPath: './manifest.json',
themeColor: '#00af87',
msTileColor: '#0b1021',
mode: 'production',
iconPaths: {
manifestCrossorigin: 'use-credentials',
favicon64: './web-icons/favicon-64x64.png',
favicon32: './web-icons/favicon-32x32.png',
maskIcon: './web-icons/dashy-logo.png',
msTileImage: './web-icons/dashy-logo.png',
},
},
};

View File

@ -7,45 +7,48 @@ const ProgressBarPlugin = require('progress-bar-webpack-plugin');
// Get current version
process.env.VUE_APP_VERSION = require('./package.json').version;
// Specify and export the main Vue app config
// Get default info for PWA
const { pwa } = require('./src/utils/defaults');
// Get base URL
const publicPath = process.env.BASE_URL || '/';
// Should enable Subresource Integrity (SRI) on link and script tags
const integrity = process.env.INTEGRITY === 'true';
// Format for progress bar, shown while app building
const progressFormat = '\x1b[1m\x1b[36mBuilding Dashy\x1b[0m '
+ '[\x1b[1m\x1b[32m:bar\x1b[0m] :percent (:elapsed seconds)';
// Webpack Config
const configureWebpack = {
performance: { hints: false },
module: {
rules: [
{ test: /.svg$/, loader: 'vue-svg-loader' },
],
},
plugins: [
new ProgressBarPlugin({ format: progressFormat }),
],
};
// Application pages
const pages = {
dashy: {
entry: 'src/main.js',
filename: 'index.html',
},
};
// Export the main Vue app config
module.exports = {
publicPath: process.env.BASE_URL,
integrity: true,
publicPath,
pwa,
integrity,
configureWebpack,
pages,
chainWebpack: config => {
config.module.rules.delete('svg');
},
configureWebpack: {
performance: { hints: false },
module: {
rules: [
{ test: /.svg$/, loader: 'vue-svg-loader' },
],
},
plugins: [
// Display progress bar while building
new ProgressBarPlugin(),
],
},
// Specify resources for PWA / mobile support
pwa: {
name: 'Dashy',
manifestPath: './manifest.json',
themeColor: '#00af87',
msTileColor: '#0b1021',
mode: 'production',
iconPaths: {
manifestCrossorigin: 'use-credentials',
favicon64: './web-icons/favicon-64x64.png',
favicon32: './web-icons/favicon-32x32.png',
maskIcon: './web-icons/dashy-logo.png',
msTileImage: './web-icons/dashy-logo.png',
},
},
// Specify page for app entry point
pages: {
dashy: {
entry: 'src/main.js',
filename: 'index.html',
},
},
};