diff --git a/.env b/.env index d18c9322..d7024eb2 100644 --- a/.env +++ b/.env @@ -20,6 +20,10 @@ # SSL_PRIV_KEY_PATH=/etc/ssl/certs/dashy-priv.key # SSL_PUB_KEY_PATH=/etc/ssl/certs/dashy-pub.pem +# If SSL enabled, choose whether or not to redirect http to https +# Defaults to true +# REDIRECT_HTTPS=true + # Usually the same as BASE_URL, but accessible in frontend # VUE_APP_DOMAIN=https://dashy.to diff --git a/.github/AUTHORS.txt b/.github/AUTHORS.txt index 72e8942b..dce2f6b4 100644 --- a/.github/AUTHORS.txt +++ b/.github/AUTHORS.txt @@ -1,3 +1,4 @@ +0n1cOn3 <0n1cOn3@gmx.ch> - 1 commits Alicia - 1 commits Begin - 1 commits David - 1 commits @@ -6,14 +7,19 @@ Devin - 1 commits FormatToday <616099456@qq.com> - 1 commits Iaroslav - 1 commits Ishan - 1 commits +Jeremy - 1 commits Kieren - 1 commits Rune - 1 commits Ryan - 1 commits Shreya - 1 commits +Singebob - 1 commits +Steven - 1 commits Xert - 1 commits deepsource-io[bot] - 1 commits icy-comet <50461557+icy-comet@users.noreply.github.com> - 1 commits jnach <33467747+jnach@users.noreply.github.com> - 1 commits +tazboyz16 - 1 commits +Alessandro - 2 commits BOZG - 2 commits Brendan <'Lear> - 2 commits Dan - 2 commits @@ -21,6 +27,7 @@ Ruben - 2 commits liss-bot <87835202+liss-bot@users.noreply.github.com> - 2 commits ᗪєνιη <υн> - 2 commits Walkx <71191962+walkxcode@users.noreply.github.com> - 3 commits +aterox - 3 commits Niklas - 4 commits Alicie - 5 commits UrekD - 5 commits @@ -31,14 +38,15 @@ Kashif - 9 commits Alicia - 16 commits github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> - 16 commits snyk-bot - 18 commits +aterox - 19 commits EVOTk <45015615+EVOTk@users.noreply.github.com> - 22 commits -repo-visualizer - 24 commits Alicia - 28 commits -snyk-bot - 29 commits -Alicia - 49 commits -Alicia - 60 commits -liss-bot - 61 commits +repo-visualizer - 30 commits +snyk-bot - 37 commits +Alicia - 61 commits +liss-bot - 75 commits Lissy93 - 78 commits +Alicia - 87 commits Lissy93 - 206 commits -Alicia - 332 commits -Alicia - 1402 commits \ No newline at end of file +Alicia - 334 commits +Alicia - 1439 commits \ No newline at end of file diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 10d57bc3..c812788b 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -1,5 +1,37 @@ # Changelog +## 🐛 2.0.6 Fixes user requested issues [PR #557](https://github.com/Lissy93/dashy/pull/557) +- Allows middle click open new tab, Re: #492 +- Implements Max redirects for status checks, Re: #494 +- Adds Gitpod config for cloud-ready IDE, Re: #497 +- Adss new screenshots to showcase, Re: #505 +- Fixes excess space below footer, Re: #522 +- Allows iframe content to be viewed full-screen, Re: #524 +- Fixes Glances widgets with Authorization headers, Re: #546 +- Adds target attribute to nav links, Re: #552 +- Removes fixed max-width on wide-screens, Re: #554 +- Adds missing type attribute to external CSS, Re: #560 +- Updates path to Keycloak API, Re: #564 +- Fixes link to @walkxhub homelab icons, Re #568 +- Fixes local image path on sub-page, Re: #570 +- Adds typecheck on edit item tags, Re: #575 +- Fixes item size in config not honored, Re: #576 + +## ✨ 2.0.5 - Bug Fixes and a few New Features + +#### Partially revert 2.0.4, fixing several issues caused by `conf.yml` not being loaded at startup. +This change requires a rebuild of the application when several options under `appConfig` are changed. +Fixes #544 #555 + +#### Several other changes since 2.0.4, including: +The `Add New Section` button on the UI editor now displays if no sections are present. #536 +When using SSL, the server can now redirect from HTTP to HTTPS. This is enabled by default when using SSL. #538 +Section context menus are now accessible on mobile, and will no longer clip off the screen. #541 +Italian translations have been added. #556 + +## ✨ 2.0.4 - Dynamic Config Loading [PR #528](https://github.com/Lissy93/dashy/pull/528) +- `conf.yml` is now loaded dynamically and the app now only needs a browser refresh on config change, not a full rebuild! + ## 🐛 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) diff --git a/.github/LATEST_CHANGELOG.md b/.github/LATEST_CHANGELOG.md new file mode 100644 index 00000000..2cad6052 --- /dev/null +++ b/.github/LATEST_CHANGELOG.md @@ -0,0 +1,16 @@ +## 🐛 Fixes user requested issues [PR #557](https://github.com/Lissy93/dashy/pull/557) +- Allows middle click open new tab, Re: #492 +- Implements Max redirects for status checks, Re: #494 +- Adds Gitpod config for cloud-ready IDE, Re: #497 +- Adss new screenshots to showcase, Re: #505 +- Fixes excess space below footer, Re: #522 +- Allows iframe content to be viewed full-screen, Re: #524 +- Fixes Glances widgets with Authorization headers, Re: #546 +- Adds target attribute to nav links, Re: #552 +- Removes fixed max-width on wide-screens, Re: #554 +- Adds missing type attribute to external CSS, Re: #560 +- Updates path to Keycloak API, Re: #564 +- Fixes link to @walkxhub homelab icons, Re #568 +- Fixes local image path on sub-page, Re: #570 +- Adds typecheck on edit item tags, Re: #575 +- Fixes item size in config not honored, Re: #576 diff --git a/.github/workflows/auto-tag-pr.yml b/.github/workflows/auto-tag-pr.yml index b828c82c..00f0108b 100644 --- a/.github/workflows/auto-tag-pr.yml +++ b/.github/workflows/auto-tag-pr.yml @@ -8,13 +8,27 @@ on: jobs: tag-pre-release: runs-on: ubuntu-latest + outputs: + tag: ${{ steps.autotag.outputs.tagname }} steps: - uses: actions/checkout@v2 - uses: butlerlogic/action-autotag@stable + id: autotag with: GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} strategy: package commit_message_template: "🔖 {{number}} {{message}} (by {{author}})\nSHA: {{sha}}\n." + github-release: + runs-on: ubuntu-latest + needs: tag-pre-release + if: ${{ needs.tag-pre-release.outputs.tag }} + steps: + - uses: actions/checkout@v2 + - uses: ncipollo/release-action@v1 + with: + token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + tag: ${{ needs.tag-pre-release.outputs.tag }} + bodyFile: ".github/LATEST_CHANGELOG.md" mark-issue-fixed: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/issue-spam-control.yml b/.github/workflows/issue-spam-control.yml index 86491c3e..13d6357b 100644 --- a/.github/workflows/issue-spam-control.yml +++ b/.github/workflows/issue-spam-control.yml @@ -17,7 +17,7 @@ jobs: name: Close issue opened by non-stargazer steps: - name: close - uses: uhyo/please-star-first@v1 + uses: uhyo/please-star-first@v1.0.1 with: token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} message: | diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 00000000..b0a726d8 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,29 @@ +# Config for running Dashy in GitPod's cloud dev environment +# Docs: https://www.gitpod.io/docs/references/gitpod-yml + +# Commands to start on workspace startup +tasks: + - init: yarn install + command: yarn dev +# Ports to expose on workspace startup +ports: + - port: 8080 # Default dev server + visibility: private + onOpen: open-preview + - port: 4000 # Default prod server + visibility: public + onOpen: open-preview +prebuilds: + # Adds 'Open in GitPod' to PRs + addBadge: true + addComment: false +vscode: + # Adds Vue.js and formatting extensions + extensions: + - octref.vetur + - dbaeumer.vscode-eslint + - streetsidesoftware.code-spell-checker + - PKief.material-icon-theme + - wix.vscode-import-cost + - oderwat.indent-rainbow + - eamodio.gitlens diff --git a/Dockerfile b/Dockerfile index ebb6f41c..8d7b15ed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,6 +40,8 @@ RUN apk add --no-cache tzdata tini # Copy built application from build phase COPY --from=BUILD_IMAGE /app ./ +# Ensure only one version of conf.yml exists +RUN rm dist/conf.yml # Finally, run start command to serve up the built application ENTRYPOINT [ "/sbin/tini", "--" ] @@ -49,4 +51,4 @@ CMD [ "yarn", "build-and-start" ] EXPOSE ${PORT} # Run simple healthchecks every 5 mins, to check that everythings still great -HEALTHCHECK --interval=5m --timeout=2s --start-period=30s CMD yarn health-check +HEALTHCHECK --interval=5m --timeout=2s --start-period=30s CMD yarn health-check \ No newline at end of file diff --git a/README.md b/README.md index 4ea8f5ab..6e53b34f 100644 --- a/README.md +++ b/README.md @@ -410,7 +410,7 @@ Dashy supports multiple languages and locales. When available, your language sho - 🇪🇸 **Spanish**: `es` - Contributed by **[@lu4t](https://github.com/lu4t)** - 🇸🇮 **Slovenian**: `sl` - Contributed by **[@UrekD](https://github.com/UrekD)** - 🇸🇪 **Swedish**: `sv` - Contributed by **[@BOZG](https://github.com/BOZG)** -- 🇮🇹 **Italian**: `it` - Machine Translated *(awaiting human review)* +- 🇮🇹 **Italian**: `it` - Contributed by **[@alexdelprete](https://github.com/alexdelprete)** - 🇵🇹 **Portuguese**: `pt` - Machine Translated *(awaiting human review)* - 🇷🇺 **Russian**: `ru` - Contributed by Anon - 🇦🇪 **Arabic**: `ar` - Contributed by Anon @@ -490,7 +490,7 @@ Huge thanks to the sponsors helping to support Dashy's development! peng1can
- peng1can + Peng1can
@@ -515,10 +515,25 @@ Huge thanks to the sponsors helping to support Dashy's development! - - Famku + + aghybris
- Famku + Aghybris +
+ + + + Byolock +
+ Byolock +
+ + + + + hugalafutro +
+ Hugalafutro
@@ -527,8 +542,7 @@ Huge thanks to the sponsors helping to support Dashy's development!
Kieren Connell - - + ratty222 diff --git a/docs/assets/CONTRIBUTORS.svg b/docs/assets/CONTRIBUTORS.svg index f6bd0f86..c1119c40 100644 --- a/docs/assets/CONTRIBUTORS.svg +++ b/docs/assets/CONTRIBUTORS.svg @@ -1,72 +1,90 @@ - + - + - + + + + - + - + - + - + - + + + + - + - + - + + + + + + + - + - + - + - + - + - + - + - + - + + + + - + - + - + + + + \ No newline at end of file diff --git a/docs/assets/repo-visualization.svg b/docs/assets/repo-visualization.svg index a664a218..b3f8a74d 100644 --- a/docs/assets/repo-visualization.svg +++ b/docs/assets/repo-visualization.svg @@ -1 +1 @@ -viewsviewsutilsutilsstylesstylesmixinsmixinscomponentscomponentsassetsassetsWorkspaceWorkspaceWidgetsWidgetsSettingsSettingsPageStrcturePageStrctureMinimalViewMinimalViewLinkItemsLinkItemsInteractiveEditorInteractiveEditorFormElementsFormElementsConfigurationConfigurationChartsChartslocaleslocalesinterface-iconsinterface-iconsemojis.jsonemojis.jsonemojis.jsonConfigSc...ConfigSc...ConfigSc...color-th...color-th...color-th...WidgetBa...WidgetBa...WidgetBa...Item.vueItem.vueItem.vuehi.jsonhi.jsonhi.json.js.json.scss.svg.vueeach dot sized by file size \ No newline at end of file +viewsviewsutilsutilsstylesstylesmixinsmixinscomponentscomponentsassetsassetsWorkspaceWorkspaceWidgetsWidgetsSettingsSettingsPageStrcturePageStrctureMinimalViewMinimalViewLinkItemsLinkItemsInteractiveEditorInteractiveEditorFormElementsFormElementsConfigurationConfigurationChartsChartslocaleslocalesinterface-iconsinterface-iconsemojis.jsonemojis.jsonemojis.jsonConfigSc...ConfigSc...ConfigSc...color-th...color-th...color-th...WidgetBa...WidgetBa...WidgetBa...Item.vueItem.vueItem.vuehi.jsonhi.jsonhi.json.js.json.scss.svg.vueeach dot sized by file size \ No newline at end of file diff --git a/docs/authentication.md b/docs/authentication.md index 4d64f753..99654981 100644 --- a/docs/authentication.md +++ b/docs/authentication.md @@ -161,6 +161,8 @@ sections: groups: ['ProductTeam'] ``` +Depending on how you're hosting Dashy and Keycloak, you may also need to set some HTTP headers, to prevent a CORS error. This would typically be the `Access-Control-Allow-Origin [URL-of Dashy]` on your Keycloak instance. See the [Setting Headers](https://github.com/Lissy93/dashy/blob/master/docs/management.md#setting-headers) guide in the management docs for more info. + Your app is now secured :) When you load Dashy, it will redirect to your Keycloak login page, and any user without valid credentials will be prevented from accessing your dashboard. From within the Keycloak console, you can then configure things like time-outs, password policies, etc. You can also backup your full Keycloak config, and it is recommended to do this, along with your Dashy config. You can spin up both Dashy and Keycloak simultaneously and restore both applications configs using a `docker-compose.yml` file, and this is recommended. diff --git a/docs/configuring.md b/docs/configuring.md index 71356c3d..82bf82d1 100644 --- a/docs/configuring.md +++ b/docs/configuring.md @@ -77,6 +77,7 @@ The following file provides a reference of all supported configuration options. --- | --- | --- | --- **`title`** | `string` | Required | The text to display on the link button **`path`** | `string` | Required | The URL to navigate to when clicked. Can be relative (e.g. `/about`) or absolute (e.g. `https://example.com` or `http://192.168.1.1`) +**`target`** | `string` | _Optional_ | The opening method (external links only). Can be either `newtab`, `sametab`, `top` or `parent`. Defaults to `newtab` **[⬆️ Back to Top](#configuring)** @@ -206,6 +207,7 @@ For more info, see the **[Authentication Docs](/docs/authentication.md)** **`statusCheckHeaders`** | `object` | _Optional_ | If you're endpoint requires any specific headers for the status checking, then define them here **`statusCheckAllowInsecure`** | `boolean` | _Optional_ | By default, any request to insecure content will be blocked. Setting this option to `true` will disable the `rejectUnauthorized` option, enabling you to ping non-HTTPS services for the current item. Defaults to `false` **`statusCheckAcceptCodes`** | `string` | _Optional_ | If your service's response code is anything other than 2xx, then you can opt to specify an alternative success code. E.g. if you expect your server to return 403, but still want the status indicator to be green, set this value to `403` +**`statusCheckMaxRedirects`** | `number` | _Optional_ | If your service redirects to another page, and you would like status checks to follow redirects, then specify the maximum number of redirects here. Defaults to `0` / will not follow redirects **`color`** | `string` | _Optional_ | An optional color for the text and font-awesome icon to be displayed in. Note that this will override the current theme and so may not display well **`backgroundColor`** | `string` | _Optional_ | An optional background fill color for the that given item. Again, this will override the current theme and so might not display well against the background **`provider`** | `string` | _Optional_ | The name of the provider for a given service, useful for when including hosted apps. In some themes, this is visible under the item name @@ -220,6 +222,7 @@ For more info, see the **[Authentication Docs](/docs/authentication.md)** **`options`** | `object` | _Optional_ | Some widgets accept either optional or required additional options. Again, see the [Widget Docs](/docs/widgets.md) for full list of options **`updateInterval`** | `number` | _Optional_ | You can keep a widget constantly updated by specifying an update interval, in seconds. See [Continuous Updates Docs](/docs/widgets.md#continuous-updates) for more info **`useProxy`** | `boolean` | _Optional_ | Some widgets make API requests to services that are not CORS-enabled. For these instances, you will need to route requests through a proxy, Dashy has a built in CORS-proxy, which you can use by setting this option to `true`. Defaults to `false`. See the [Proxying Requests Docs](/docs/widgets.md#proxying-requests) for more info +**`timeout`** | `number` | _Optional_ | Request timeout in milliseconds, defaults to ½ a second (`500`) **[⬆️ Back to Top](#configuring)** diff --git a/docs/credits.md b/docs/credits.md index daa09dd5..4198e296 100644 --- a/docs/credits.md +++ b/docs/credits.md @@ -8,7 +8,7 @@ peng1can
- peng1can + Peng1can
@@ -33,10 +33,25 @@ - - Famku + + aghybris
- Famku + Aghybris +
+ + + + Byolock +
+ Byolock +
+ + + + + hugalafutro +
+ Hugalafutro
@@ -45,8 +60,7 @@
Kieren Connell - - + ratty222 @@ -96,6 +110,13 @@ EVOTk + + + Ateroz +
+ Kenneth Church +
+ snyk-bot @@ -109,15 +130,15 @@
Kashif Sohail
- + + evroon
Erik Vroon
- - + UrekD @@ -139,13 +160,21 @@ ᗪєνιη ᗷυнʟ + + + alexdelprete +
+ Alessandro Del Prete +
+ BOZG
Stephen Rigney
- + + daentech @@ -159,8 +188,21 @@
Rúben Silva
- - + + + + Singebob +
+ Jeremy Chauvin +
+ + + + 0n1cOn3 +
+ 0n1cOn3 +
+ BeginCI @@ -174,7 +216,8 @@
David
- + + deepsourcebot @@ -202,8 +245,7 @@
Ishan Jain
- - + KierenConnell @@ -217,7 +259,8 @@
Rubjo
- + + turnrye @@ -232,6 +275,13 @@ Shreya Roy + + + StevKast +
+ Steven Kast +
+ XertDev @@ -246,14 +296,6 @@ Aniket Teredesai - - - - jnach -
- Jnach -
- diff --git a/docs/deployment.md b/docs/deployment.md index 83f76830..df9555bc 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -89,7 +89,7 @@ services: 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 + # - /root/my-config.yml:/public/conf.yml ports: - 4000:80 # Set any environmental variables @@ -122,8 +122,27 @@ If you are building from source, and would like to use one of the [other Dockerf ## Synology NAS -// TODO +Installing dashy is really simply and fast: +1. Install Docker via Synology ```Package Center```. +2. Go to ```File Station``` and open the ```docker``` folder. Inside the docker folder, create one new folder and name it ```dashy```. + > Note: Be careful to enter only lowercase, not uppercase letters. +3. Go to Control Panel / Task Scheduler / Create / Scheduled Task / User-defined script. +4. Once you click on ```User-defined``` script a new window will open. +5. Follow the instructions below: +6. General: In the Task field type in Install dashy. Uncheck “Enabled” option. Select root User. +7. Schedule: Select Run on the following date then select “Do not repeat“. +8. Task Settings: Check “Send run details by email“, add your email then copy paste the code below in the Run command area. After that click OK. + +``` +docker run -d \ + -p 4000:80 \ + -v /volume1/docker/dashy/my-local-conf.yml:/app/public/conf.yml \ + --name dashy \ + --restart=always \ + lissy93/dashy:latest +``` +dashy should be up within 1-2min after you've started the install task procedure --- ## Build from Source @@ -189,7 +208,7 @@ https://vercel.com/new/project?template=https://github.com/lissy93/dashy #### DigitalOcean [![Deploy to DO](https://i.ibb.co/PFt0PkB/deploy-digital-ocean-button.png)](https://cloud.digitalocean.com/apps/new?repo=https://github.com/lissy93/dashy/tree/deploy_digital-ocean&refcode=3838338e7f79) -[DigitalOcan](https://www.digitalocean.com/) is a cloud service providing affordable developer-friendly virtual machines from $5/month. But they also have an app platform, where you can run web apps, static sites, APIs and background workers. CDN-backed static sites are free for personal use. +[DigitalOcean](https://www.digitalocean.com/) is a cloud service providing affordable developer-friendly virtual machines from $5/month. But they also have an app platform, where you can run web apps, static sites, APIs and background workers. CDN-backed static sites are free for personal use. ``` https://cloud.digitalocean.com/apps/new?repo=https://github.com/lissy93/dashy/tree/deploy_digital-ocean diff --git a/docs/management.md b/docs/management.md index d38881e4..2d1d12c0 100644 --- a/docs/management.md +++ b/docs/management.md @@ -15,6 +15,7 @@ _The following article is a primer on managing self-hosted apps. It covers every - [Authentication](#authentication) - [Managing with Compose](#managing-containers-with-docker-compose) - [Environmental Variables](#passing-in-environmental-variables) +- [Setting Headers](#setting-headers) - [Remote Access](#remote-access) - [Custom Domain](#custom-domain) - [Securing Containers](#container-security) @@ -288,6 +289,89 @@ If you've got many environmental variables, you might find it useful to put them --- +## Setting Headers + +Any external requests made to a different origin (app/ service under a different domain) will be blocked if the correct headers are not specified. This is known as [Cross-Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) (CORS) and is a security feature built into modern browsers. + +If you see a CORS error in your console, this can be easily fixed by setting the correct headers. This is not a bug with Dashy, so please don't raise it as a bug! + +### Example Headers +- [Caddy](#caddy) +- [NGINX](#nginx) +- [Træfɪk](#traefik) +- [HAProxy](#haproxy) +- [Apache](#apache) + +_The following section briefly outlines how you can set headers for common web proxies/ servers. More info can be found in the documentation for the proxy that you are using, or in the [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)._ + +These examples are using: +- `Access-Control-Allow-Origin` header, but depending on what type of content you are enabling, this will vary. For example, to allow a site to be loaded in an iframe (for the modal or workspace views) you would use `X-Frame-Options`. +- The domain root (`/`), if your're hosting from a sub-page, replace that with your path. +- A wildcard (`*`), which would allow access from traffic on any domain, this is discorouaged, and you should replace it with the URL where you are hosting Dashy. Note that for requests that transport sensitive info, like credentials (e.g. Keycloak login), the wildcard is [disallowed all together](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#requests_with_credentials) and will be blocked. + +#### Caddy + +> See [Caddy `header` docs](https://caddyserver.com/docs/caddyfile/directives/header) for more info. + +``` +headers / { + Access-Control-Allow-Origin * +} +``` + +#### NGINX + +> See [NGINX `ngx_http_headers_module` docs](https://nginx.org/en/docs/http/ngx_http_headers_module.html) for more info. + +``` +location / { + add_header Access-Control-Allow-Origin *; +} +``` + +Note this can also be done through the UI, using NGINX Proxy Manager. + +#### Traefik + +> See [Træfɪk CORS headers docs](https://doc.traefik.io/traefik/middlewares/http/headers/#cors-headers) for more info. + +``` +labels: + - "traefik.http.middlewares.testheader.headers.accesscontrolallowmethods=GET,OPTIONS,PUT" + - "traefik.http.middlewares.testheader.headers.accesscontrolalloworiginlist=https://foo.bar.org,https://example.org" + - "traefik.http.middlewares.testheader.headers.accesscontrolmaxage=100" + - "traefik.http.middlewares.testheader.headers.addvaryheader=true" +``` + +#### HAProxy + +> See [HAProxy Rewrite Response Docs](https://www.haproxy.com/documentation/hapee/latest/traffic-routing/rewrites/rewrite-responses/) for more info. + +``` +/ + http-response add-header Access-Control-Allow-Origin * +``` + +#### Apache + +> See [Apache `mode_headers` docs](https://httpd.apache.org/docs/current/mod/mod_headers.html) for more info. + +``` +Header always set Access-Control-Allow-Origin "*" +``` + +#### Squid + +> See [Squid `request_header_access` docs](http://www2.gr.squid-cache.org/Doc/config/request_header_access/) for more info. + +``` +request_header_access Authorization allow all +``` + +**[⬆️ Back to Top](#management)** + +--- + ## Remote Access - [WireGuard](#wireguard) diff --git a/docs/showcase.md b/docs/showcase.md index b9eeb868..be6602d8 100644 --- a/docs/showcase.md +++ b/docs/showcase.md @@ -16,6 +16,13 @@ --- +### Hugalafutro Dashy +> By [@hugalafutro](https://github.com/hugalafutro) [#505](https://github.com/Lissy93/dashy/discussions/505) + +[![hugalafutro-dashy-screenshot](https://i.ibb.co/PDpLDKS/hugalafutro-dashy.gif)](https://i.ibb.co/PDpLDKS/hugalafutro-dashy.gif) + +--- + ### Networking Services > By [@Lissy93](https://github.com/lissy93) @@ -126,6 +133,13 @@ --- +### Croco_Grievous +> By [u/Croco_Grievous](https://www.reddit.com/user/Croco_Grievous/) via [reddit](https://www.reddit.com/r/selfhosted/comments/t4xk3z/everything_started_with_pihole_on_a_raspberry_pi/) + +![screenshot-croco-grievous-dashy](https://i.ibb.co/59XR8KL/dashy-Croco-Grievous.png) + +--- + ### Crypto Dash > Example usage of widgets to monitor cryptocurrencies news, prices and data. Config is [available here](https://gist.github.com/Lissy93/000f712a5ce98f212817d20bc16bab10#file-example-8-dashy-crypto-widgets-conf-yml) @@ -134,6 +148,13 @@ --- +### Stefantigro +> By [u/stefantigro](https://www.reddit.com/user/stefantigro/) via [reddit](https://www.reddit.com/r/selfhosted/comments/t5oril/been_selfhosting_close_to_half_a_year_now_all/) + +![screenshot-stefantigro-dashy](https://i.ibb.co/1Kb43Yy/dashy-stefantigro.png) + +--- + ### Yet Another Homelab ![screenshot-yet-another-homelab](https://raw.githubusercontent.com/Lissy93/dashy/master/docs/showcase/9-home-lab-oblivion.png) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 673fa757..9b27d28a 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -9,6 +9,9 @@ - [404 On Static Hosting](#404-on-static-hosting) - [Yarn Build or Run Error](#yarn-error) - [Auth Validation Error: "should be object"](#auth-validation-error-should-be-object) +- [App Not Starting After Update to 2.0.4](#app-not-starting-after-update-to-204) +- [Keycloak Redirect Error](#keycloak-redirect-error) +- [Docker Directory Error](#docker-directory) - [Config Not Updating](#config-not-updating) - [Config Still not Updating](#config-still-not-updating) - [Styles and Assets not Updating](#styles-and-assets-not-updating) @@ -124,6 +127,53 @@ auth: --- +## App Not Starting After Update to 2.0.4 + +Version 2.0.4 introduced changes to how the config is read, and the app is build. If you were previously mounting `/public` as a volume, then this will over-write the build app, preventing it from starting. The solution is to just pass in the file(s) / sub-directories that you need. For example: + +```yaml +volumes: +- /srv/dashy/conf.yml:/app/public/conf.yml +- /srv/dashy/item-icons:/app/public/item-icons +``` + +--- + +## Keycloak Redirect Error + +Check the [browser's console output](#how-to-open-browser-console), if you've not set any headers, you will likely see a CORS error here, which would be the source of the issue. + +You need to allow Dashy to make requests to Keycloak, and Keycloak to redirect to Dashy. The way you do this depends on how you're hosting these applications / which proxy you are using, and examples can be found in the [Management Docs](/docs/management.md#setting-headers). + +For example, add the access control header to Keycloak, like: + +`Access-Control-Allow-Origin [URL-of Dashy]` + +Note that for requests that transport sensitive info like credentials, setting the accept header to a wildcard (`*`) is not allowed - see [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#requests_with_credentials), so you will need to specify the actual URL. + +You should also ensure that Keycloak is correctly configured, with a user, realm and application, and be sure that you have set a valid redirect URL in Keycloak ([screenshot](https://user-images.githubusercontent.com/1862727/148599768-db4ee4f8-72c5-402d-8f00-051d999e6267.png)). + +For more details on how to set headers, see the [Example Headers](/docs/management.md#setting-headers) in the management docs, or reference the documentation for your proxy. + +See also: #479, #409, #507, #491, #341, #520 + +--- + +## Docker Directory + +``` +Error response from daemon: OCI runtime create failed: container_linux.go:380: +starting container process caused: process_linux.go:545: container init caused: +rootfs_linux.go:76: mounting "/home/ubuntu/my-conf.yml" to rootfs at +"/app/public/conf.yml" caused: mount through procfd: not a directory: +unknown: Are you trying to mount a directory onto a file (or vice-versa)? +Check if the specified host path exists and is the expected type. +``` + +If you get an error similar to the one above, you are mounting a directory to the config file's location, when a plain file is expected. Create a YAML file, (`touch my-conf.yml`), populate it with a sample config, then pass it as a volume: `-v ./my-local-conf.yml:/app/public/conf.yml` + +--- + ## Config Not Updating Dashy has the option to save settings and config locally, in browser storage. Anything here will take precedence over whatever is in your config file, sometimes with unintended consequences. If you've updated the config file manually, and are not seeing changes reflected in the UI, then try visiting the site in Incognito mode. If that works, then the solution is just to clear local storage. This can be done from the config menu, under "Clear Local Settings". @@ -272,6 +322,8 @@ or Access-Control-Allow-Origin: * ``` +For more info on how to set headers, see: [Setting Headers](/docs/management.md#setting-headers) in the management docs + #### Option 3 - Proxying Request You can route requests through Dashy's built-in CORS proxy. Instructions and more details can be found [here](/docs/widgets.md#proxying-requests). If you don't have control over the target origin, and you are running Dashy either through Docker, with the Node server or on Netlify, then this solution will work for you. diff --git a/docs/widgets.md b/docs/widgets.md index 1059d7ad..3c005c49 100644 --- a/docs/widgets.md +++ b/docs/widgets.md @@ -1289,6 +1289,7 @@ All Glance's based widgets require a `hostname`. All other parameters are option **`apiVersion`** | `string` | _Optional_ | Specify an API version, defaults to V `3`. Note that support for older versions is limited **`limit`** | `number` | _Optional_ | For widgets that show a time-series chart, optionally limit the number of data points returned. A higher number will show more historical results, but will take longer to load. A value between 300 - 800 is usually optimal +Note that if auth is configured, requests must be proxied with `useProxy: true` ##### Info - **CORS**: 🟢 Enabled - **Auth**: 🟠 Optional @@ -1726,6 +1727,12 @@ Vary: Origin --- +### Setting Timeout + +Default timeout is ½ a second. This can be overridden with the `timeout` attribute on a widget, specified as an integer in milliseconds. + +--- + ### Widget Styling Like elsewhere in Dashy, all colours can be easily modified with CSS variables. @@ -1791,8 +1798,6 @@ Alternatively, for displaying simple data, you could also just use the either th Suggestions for widget ideas are welcome. But there is no guarantee that I will build your widget idea. -You can suggest a widget [here](https://git.io/Jygo3), please star the repo before submitting a ticket. - Please only request widgets for services that: - Have a publicly accessible API - Are CORS and HTTPS enabled @@ -1800,6 +1805,8 @@ Please only request widgets for services that: - Allow for use in their Terms of Service - Would be useful for other users +You can suggest a widget [here](https://git.io/Jygo3), please star the repo before submitting a ticket. If you are a monthly GitHub sponsor, I will happily build out a custom widget for any service that meets the above criteria, usually 2 within weeks of initial request. + For services that are not officially supported, it is likely still possible to display data using either the [iframe](#iframe-widget), [embed](#html-embedded-widget) or [API response](#api-response) widgets. For more advanced features, like charts and action buttons, you could also build your own widget, using [this tutorial](/docs/development-guides.md#building-a-widget), it's fairly straight forward, and you can use an [existing widget](https://github.com/Lissy93/dashy/tree/master/src/components/Widgets) (or [this example](https://git.io/JygKI)) as a template. --- diff --git a/package.json b/package.json index adbdd615..464520b5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Dashy", - "version": "2.0.3", + "version": "2.0.6", "license": "MIT", "main": "server", "author": "Alicia Sykes (https://aliciasykes.com)", @@ -18,7 +18,7 @@ }, "dependencies": { "@formschema/native": "^2.0.0-beta.6", - "@sentry/tracing": "^6.17.4", + "@sentry/tracing": "^6.17.5", "@sentry/vue": "^6.17.4", "ajv": "^8.10.0", "axios": "^0.25.0", @@ -31,7 +31,7 @@ "register-service-worker": "^1.7.2", "remedial": "^1.0.8", "rsup-progress": "^3.0.0", - "simple-icons": "^6.8.0", + "simple-icons": "^6.9.0", "v-jsoneditor": "^1.4.5", "v-tooltip": "^2.1.3", "vue": "^2.6.14", diff --git a/server.js b/server.js index 4cef9e23..4da6dafb 100644 --- a/server.js +++ b/server.js @@ -66,9 +66,11 @@ const printWarning = (msg, error) => { const method = (m, mw) => (req, res, next) => (req.method === m ? mw(req, res, next) : next()); const app = express() + // Load SSL redirection middleware + .use(sslServer.middleware) // Serves up static files .use(express.static(path.join(__dirname, 'dist'))) - .use(express.static(path.join(__dirname, 'public'), { index: 'initialization.html' })) + .use(express.static(path.join(__dirname, 'public'))) // Load middlewares for parsing JSON, and supporting HTML5 history routing .use(express.json({ limit: '1mb' })) .use(history()) @@ -128,4 +130,4 @@ http.createServer(app) }); /* Check, and if possible start SSL server too */ -sslServer(app); +sslServer.startSSLServer(app); diff --git a/services/print-message.js b/services/print-message.js index a1017d8c..69feb4de 100644 --- a/services/print-message.js +++ b/services/print-message.js @@ -28,9 +28,6 @@ module.exports = (ip, port, isDocker) => { + `${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.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 { // Prepare message for users running app on bare metal @@ -38,8 +35,6 @@ module.exports = (ip, port, isDocker) => { + `┃ ${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}${chars.RESET}`; } // Make some sexy ascii art ;) diff --git a/services/ssl-server.js b/services/ssl-server.js index a3dada05..13695bd6 100644 --- a/services/ssl-server.js +++ b/services/ssl-server.js @@ -5,36 +5,48 @@ const https = require('https'); const promise = util.promisify; const stat = promise(fs.stat); -module.exports = (app) => { - const httpsCerts = { - private: process.env.SSL_PRIV_KEY_PATH || '/etc/ssl/certs/dashy-priv.key', - public: process.env.SSL_PUB_KEY_PATH || '/etc/ssl/certs/dashy-pub.pem', - }; +const httpsCerts = { + private: process.env.SSL_PRIV_KEY_PATH || '/etc/ssl/certs/dashy-priv.key', + public: process.env.SSL_PUB_KEY_PATH || '/etc/ssl/certs/dashy-pub.pem', +}; - const isDocker = !!process.env.IS_DOCKER; - const SSLPort = process.env.SSL_PORT || (isDocker ? 443 : 4001); +const isDocker = !!process.env.IS_DOCKER; +const SSLPort = process.env.SSL_PORT || (isDocker ? 443 : 4001); +const redirectHttps = process.env.REDIRECT_HTTPS ? process.env.REDIRECT_HTTPS : true; - const printSuccess = () => { - console.log(`🔐 HTTPS server successfully started (port: ${SSLPort} ${isDocker ? 'of container' : ''})`); - }; +const printNotSoGood = (msg) => { + console.log(`SSL Not Enabled: ${msg}`); +}; - const printNotSoGood = (msg) => { - console.log(`SSL Not Enabled: ${msg}`); - }; +const printSuccess = () => { + console.log(`🔐 HTTPS server successfully started (port: ${SSLPort} ${isDocker ? 'of container' : ''})`); +}; - /* Starts SSL-secured node server */ - const startSSLServer = () => { +// Check if the SSL certs are present and SSL should be enabled +let enableSSL = false; +stat(httpsCerts.public).then(() => { + stat(httpsCerts.private).then(() => { + enableSSL = true; + }).catch(() => { printNotSoGood('Private key not present'); }); +}).catch(() => { printNotSoGood('Public key not present'); }); + +const startSSLServer = (app) => { + // If SSL should be enabled, create a secured server and start it + if (enableSSL) { const httpsServer = https.createServer({ key: fs.readFileSync(httpsCerts.private), cert: fs.readFileSync(httpsCerts.public), }, app); httpsServer.listen(SSLPort, () => { printSuccess(); }); - }; - - /* Check if SSL certs present, if so also start the HTTPS server */ - stat(httpsCerts.public).then(() => { - stat(httpsCerts.private).then(() => { - startSSLServer(); - }).catch(() => { printNotSoGood('Private key not present'); }); - }).catch(() => { printNotSoGood('Public key not present'); }); + } }; + +const middleware = (req, res, next) => { + if (enableSSL && redirectHttps && req.protocol === 'http') { + res.redirect(`https://${req.hostname + ((SSLPort === 443) ? '' : `:${SSLPort}`) + req.url}`); + } else { + next(); + } +}; + +module.exports = { startSSLServer, middleware }; diff --git a/services/status-check.js b/services/status-check.js index 6f52482c..1c725eb4 100644 --- a/services/status-check.js +++ b/services/status-check.js @@ -28,15 +28,23 @@ const makeErrorMessage2 = (data) => '❌ Service Error - ' + `${data.status} - ${data.statusText}`; /* Kicks of a HTTP request, then formats and renders results */ -const makeRequest = (url, headers, insecure, acceptCodes, render) => { +const makeRequest = (url, options, render) => { + console.log(options); + const { + headers, enableInsecure, acceptCodes, maxRedirects, + } = options; const validCodes = acceptCodes && acceptCodes !== 'null' ? acceptCodes : null; const startTime = new Date(); const requestMaker = axios.create({ httpsAgent: new https.Agent({ - rejectUnauthorized: !insecure, + rejectUnauthorized: !enableInsecure, }), }); - requestMaker.get(url, { headers }) + requestMaker.request({ + url, + headers, + maxRedirects, + }) .then((response) => { const statusCode = response.status; const { statusText } = response; @@ -100,9 +108,13 @@ module.exports = (paramStr, render) => { const params = new URLSearchParams(paramStr); const url = decodeURIComponent(params.get('url')); const acceptCodes = decodeURIComponent(params.get('acceptCodes')); + const maxRedirects = decodeURIComponent(params.get('maxRedirects')) || 0; const headers = decodeHeaders(params.get('headers')); const enableInsecure = !!params.get('enableInsecure'); if (!url || url === 'undefined') immediateError(render); - makeRequest(url, headers, enableInsecure, acceptCodes, render); + const options = { + headers, enableInsecure, acceptCodes, maxRedirects, + }; + makeRequest(url, options, render); } }; diff --git a/src/App.vue b/src/App.vue index d669556f..382e1719 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,10 +1,10 @@ diff --git a/src/components/MinimalView/MinimalSection.vue b/src/components/MinimalView/MinimalSection.vue index d561f3ca..5dd244b5 100644 --- a/src/components/MinimalView/MinimalSection.vue +++ b/src/components/MinimalView/MinimalSection.vue @@ -19,6 +19,7 @@ :enableStatusCheck="shouldEnableStatusCheck(item.statusCheck)" :statusCheckAllowInsecure="item.statusCheckAllowInsecure" :statusCheckAcceptCodes="item.statusCheckAcceptCodes" + :statusCheckMaxRedirects="item.statusCheckMaxRedirects" :statusCheckInterval="getStatusCheckInterval()" @itemClicked="$emit('itemClicked')" @triggerModal="triggerModal" diff --git a/src/components/PageStrcture/Nav.vue b/src/components/PageStrcture/Nav.vue index 09261b52..62bfc3eb 100644 --- a/src/components/PageStrcture/Nav.vue +++ b/src/components/PageStrcture/Nav.vue @@ -5,15 +5,23 @@ @click="navVisible = !navVisible" /> @@ -43,6 +51,16 @@ export default { return screenWidth && screenWidth < 600; }, isUrl: (str) => new RegExp(/(http|https):\/\/(\S+)(:[0-9]+)?/).test(str), + determineTarget(link) { + if (!link.target) return '_blank'; + switch (link.target) { + case 'sametab': return '_self'; + case 'newtab': return '_blank'; + case 'parent': return '_parent'; + case 'top': return '_top'; + default: return undefined; + } + }, }, }; diff --git a/src/components/Widgets/WidgetBase.vue b/src/components/Widgets/WidgetBase.vue index 63b13be5..e6f80c3a 100644 --- a/src/components/Widgets/WidgetBase.vue +++ b/src/components/Widgets/WidgetBase.vue @@ -476,10 +476,13 @@ export default { /* Returns users specified widget options, or empty object */ widgetOptions() { const options = this.widget.options || {}; + const timeout = this.widget.timeout || 500; const useProxy = this.appConfig.widgetsAlwaysUseProxy || !!this.widget.useProxy; const updateInterval = this.widget.updateInterval !== undefined ? this.widget.updateInterval : null; - return { useProxy, updateInterval, ...options }; + return { + timeout, useProxy, updateInterval, ...options, + }; }, /* A unique string to reference the widget by */ widgetRef() { diff --git a/src/components/Workspace/WebContent.vue b/src/components/Workspace/WebContent.vue index e7465a3a..69cc2832 100644 --- a/src/components/Workspace/WebContent.vue +++ b/src/components/Workspace/WebContent.vue @@ -1,6 +1,6 @@