🔀 Rebased from master

This commit is contained in:
Alicia Sykes 2022-03-29 00:58:44 +01:00
commit 3694a3ad03
50 changed files with 865 additions and 249 deletions

4
.env
View File

@ -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

22
.github/AUTHORS.txt vendored
View File

@ -1,3 +1,4 @@
0n1cOn3 <0n1cOn3@gmx.ch> - 1 commits
Alicia <liss-bot@d0h.co> - 1 commits
Begin <support@begin.com> - 1 commits
David <skaarj1989@gmail.com> - 1 commits
@ -6,14 +7,19 @@ Devin <uh> - 1 commits
FormatToday <616099456@qq.com> - 1 commits
Iaroslav <ronski> - 1 commits
Ishan <ai> - 1 commits
Jeremy <hauvi> - 1 commits
Kieren <onnel> - 1 commits
Rune <jørnerå> - 1 commits
Ryan <urne> - 1 commits
Shreya <o> - 1 commits
Singebob <jeremy.chauvin@systeme-u.fr> - 1 commits
Steven <as> - 1 commits
Xert <xertdev@gmail.com> - 1 commits
deepsource-io[bot] <deepsource-io[bot]@users.noreply.github.com> - 1 commits
icy-comet <50461557+icy-comet@users.noreply.github.com> - 1 commits
jnach <33467747+jnach@users.noreply.github.com> - 1 commits
tazboyz16 <tazboyz_16@yahoo.com> - 1 commits
Alessandro <e> - 2 commits
BOZG <sr@bozg.se> - 2 commits
Brendan <&#39;Lear> - 2 commits
Dan <ilber> - 2 commits
@ -21,6 +27,7 @@ Ruben <ilv> - 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 <kenneth@kenneth.church> - 3 commits
Niklas <abe> - 4 commits
Alicie <gh@d0h.co> - 5 commits
UrekD <urek.denis@gmail.com> - 5 commits
@ -31,14 +38,15 @@ Kashif <ohai> - 9 commits
Alicia <yke> - 16 commits
github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> - 16 commits
snyk-bot <snyk-bot@users.noreply.github.com> - 18 commits
aterox <church.kennetha@gmail.com> - 19 commits
EVOTk <45015615+EVOTk@users.noreply.github.com> - 22 commits
repo-visualizer <repo-visualizer@users.noreply.github.com> - 24 commits
Alicia <yke> - 28 commits
snyk-bot <snyk-bot@snyk.io> - 29 commits
Alicia <o> - 49 commits
Alicia <yke> - 60 commits
liss-bot <liss-bot@d0h.co> - 61 commits
repo-visualizer <repo-visualizer@users.noreply.github.com> - 30 commits
snyk-bot <snyk-bot@snyk.io> - 37 commits
Alicia <o> - 61 commits
liss-bot <liss-bot@d0h.co> - 75 commits
Lissy93 <gh@d0h.co> - 78 commits
Alicia <yke> - 87 commits
Lissy93 <Lissy93@users.noreply.github.com> - 206 commits
Alicia <yke> - 332 commits
Alicia <yke> - 1402 commits
Alicia <yke> - 334 commits
Alicia <yke> - 1439 commits

32
.github/CHANGELOG.md vendored
View File

@ -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)

16
.github/LATEST_CHANGELOG.md vendored Normal file
View File

@ -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

View File

@ -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:

View File

@ -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: |

29
.gitpod.yml Normal file
View File

@ -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

View File

@ -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

View File

@ -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!
<a href="https://github.com/peng1can">
<img src="https://avatars.githubusercontent.com/u/225854?v=4" width="80;" alt="peng1can"/>
<br />
<sub><b>peng1can</b></sub>
<sub><b>Peng1can</b></sub>
</a>
</td>
<td align="center">
@ -515,10 +515,25 @@ Huge thanks to the sponsors helping to support Dashy's development!
</a>
</td>
<td align="center">
<a href="https://github.com/Famku">
<img src="https://avatars.githubusercontent.com/u/27890413?v=4" width="80;" alt="Famku"/>
<a href="https://github.com/aghybris">
<img src="https://avatars.githubusercontent.com/u/11677119?v=4" width="80;" alt="aghybris"/>
<br />
<sub><b>Famku</b></sub>
<sub><b>Aghybris</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Byolock">
<img src="https://avatars.githubusercontent.com/u/25748003?v=4" width="80;" alt="Byolock"/>
<br />
<sub><b>Byolock</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/hugalafutro">
<img src="https://avatars.githubusercontent.com/u/30209689?v=4" width="80;" alt="hugalafutro"/>
<br />
<sub><b>Hugalafutro</b></sub>
</a>
</td>
<td align="center">
@ -527,8 +542,7 @@ Huge thanks to the sponsors helping to support Dashy's development!
<br />
<sub><b>Kieren Connell</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/ratty222">
<img src="https://avatars.githubusercontent.com/u/92832598?u=137b65530cbd5f5af9c24cde51baa6cc77cc934b&v=4" width="80;" alt="ratty222"/>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 4.8 MiB

After

Width:  |  Height:  |  Size: 5.9 MiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 86 KiB

View File

@ -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.

View File

@ -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)**

View File

@ -8,7 +8,7 @@
<a href="https://github.com/peng1can">
<img src="https://avatars.githubusercontent.com/u/225854?v=4" width="80;" alt="peng1can"/>
<br />
<sub><b>peng1can</b></sub>
<sub><b>Peng1can</b></sub>
</a>
</td>
<td align="center">
@ -33,10 +33,25 @@
</a>
</td>
<td align="center">
<a href="https://github.com/Famku">
<img src="https://avatars.githubusercontent.com/u/27890413?v=4" width="80;" alt="Famku"/>
<a href="https://github.com/aghybris">
<img src="https://avatars.githubusercontent.com/u/11677119?v=4" width="80;" alt="aghybris"/>
<br />
<sub><b>Famku</b></sub>
<sub><b>Aghybris</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Byolock">
<img src="https://avatars.githubusercontent.com/u/25748003?v=4" width="80;" alt="Byolock"/>
<br />
<sub><b>Byolock</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/hugalafutro">
<img src="https://avatars.githubusercontent.com/u/30209689?v=4" width="80;" alt="hugalafutro"/>
<br />
<sub><b>Hugalafutro</b></sub>
</a>
</td>
<td align="center">
@ -45,8 +60,7 @@
<br />
<sub><b>Kieren Connell</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/ratty222">
<img src="https://avatars.githubusercontent.com/u/92832598?u=137b65530cbd5f5af9c24cde51baa6cc77cc934b&v=4" width="80;" alt="ratty222"/>
@ -96,6 +110,13 @@
<sub><b>EVOTk</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/Ateroz">
<img src="https://avatars.githubusercontent.com/u/5369885?v=4" width="80;" alt="Ateroz"/>
<br />
<sub><b>Kenneth Church</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/snyk-bot">
<img src="https://avatars.githubusercontent.com/u/19733683?v=4" width="80;" alt="snyk-bot"/>
@ -109,15 +130,15 @@
<br />
<sub><b>Kashif Sohail</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/evroon">
<img src="https://avatars.githubusercontent.com/u/11857441?v=4" width="80;" alt="evroon"/>
<br />
<sub><b>Erik Vroon</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/UrekD">
<img src="https://avatars.githubusercontent.com/u/38784343?v=4" width="80;" alt="UrekD"/>
@ -139,13 +160,21 @@
<sub><b>ᗪєνιη ᗷυнʟ</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/alexdelprete">
<img src="https://avatars.githubusercontent.com/u/7027842?v=4" width="80;" alt="alexdelprete"/>
<br />
<sub><b>Alessandro Del Prete</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/BOZG">
<img src="https://avatars.githubusercontent.com/u/6022344?v=4" width="80;" alt="BOZG"/>
<br />
<sub><b>Stephen Rigney</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/daentech">
<img src="https://avatars.githubusercontent.com/u/358678?v=4" width="80;" alt="daentech"/>
@ -159,8 +188,21 @@
<br />
<sub><b>Rúben Silva</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/Singebob">
<img src="https://avatars.githubusercontent.com/u/24290044?v=4" width="80;" alt="Singebob"/>
<br />
<sub><b>Jeremy Chauvin</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/0n1cOn3">
<img src="https://avatars.githubusercontent.com/u/27576311?v=4" width="80;" alt="0n1cOn3"/>
<br />
<sub><b>0n1cOn3</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/BeginCI">
<img src="https://avatars.githubusercontent.com/u/57495754?v=4" width="80;" alt="BeginCI"/>
@ -174,7 +216,8 @@
<br />
<sub><b>David</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/deepsourcebot">
<img src="https://avatars.githubusercontent.com/u/60907429?v=4" width="80;" alt="deepsourcebot"/>
@ -202,8 +245,7 @@
<br />
<sub><b>Ishan Jain</b></sub>
</a>
</td></tr>
<tr>
</td>
<td align="center">
<a href="https://github.com/KierenConnell">
<img src="https://avatars.githubusercontent.com/u/46445781?v=4" width="80;" alt="KierenConnell"/>
@ -217,7 +259,8 @@
<br />
<sub><b>Rubjo</b></sub>
</a>
</td>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/turnrye">
<img src="https://avatars.githubusercontent.com/u/701035?v=4" width="80;" alt="turnrye"/>
@ -232,6 +275,13 @@
<sub><b>Shreya Roy</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/StevKast">
<img src="https://avatars.githubusercontent.com/u/17804308?v=4" width="80;" alt="StevKast"/>
<br />
<sub><b>Steven Kast</b></sub>
</a>
</td>
<td align="center">
<a href="https://github.com/XertDev">
<img src="https://avatars.githubusercontent.com/u/16572811?v=4" width="80;" alt="XertDev"/>
@ -246,14 +296,6 @@
<sub><b>Aniket Teredesai</b></sub>
</a>
</td></tr>
<tr>
<td align="center">
<a href="https://github.com/jnach">
<img src="https://avatars.githubusercontent.com/u/33467747?v=4" width="80;" alt="jnach"/>
<br />
<sub><b>Jnach</b></sub>
</a>
</td></tr>
</table>
<!-- readme: contributors -end -->

View File

@ -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

View File

@ -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)

View File

@ -16,6 +16,13 @@
---
### Hugalafutro Dashy
> By [@hugalafutro](https://github.com/hugalafutro) <sup>[#505](https://github.com/Lissy93/dashy/discussions/505)</sup>
[![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/) <sup>via [reddit](https://www.reddit.com/r/selfhosted/comments/t4xk3z/everything_started_with_pihole_on_a_raspberry_pi/)</sup>
![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/) <sup>via [reddit](https://www.reddit.com/r/selfhosted/comments/t5oril/been_selfhosting_close_to_half_a_year_now_all/)</sup>
![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)

View File

@ -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.

View File

@ -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.
---

View File

@ -1,6 +1,6 @@
{
"name": "Dashy",
"version": "2.0.3",
"version": "2.0.6",
"license": "MIT",
"main": "server",
"author": "Alicia Sykes <alicia@omg.lol> (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",

View File

@ -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);

View File

@ -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 ;)

View File

@ -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 };

View File

@ -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);
}
};

View File

@ -1,10 +1,10 @@
<template>
<div id="dashy">
<div id="dashy" :style="topLevelStyleModifications">
<EditModeTopBanner v-if="isEditMode" />
<LoadingScreen :isLoading="isLoading" v-if="shouldShowSplash" />
<Header :pageInfo="pageInfo" />
<router-view />
<Footer :text="footerText" v-if="visibleComponents.footer" />
<router-view v-if="!isFetching" />
<Footer :text="footerText" v-if="visibleComponents.footer && !isFetching" />
</div>
</template>
<script>
@ -33,6 +33,7 @@ export default {
data() {
return {
isLoading: true, // Set to false after mount complete
isFetching: true, // Set to false after the conf has been fetched
};
},
watch: {
@ -40,6 +41,9 @@ export default {
// When in edit mode, show confirmation dialog on page exit
window.onbeforeunload = isEditMode ? this.confirmExit : null;
},
config() {
this.isFetching = false;
},
},
computed: {
/* If the user has specified custom text for footer - get it */
@ -68,9 +72,17 @@ export default {
isEditMode() {
return this.$store.state.editMode;
},
},
created() {
this.$store.dispatch(Keys.INITIALIZE_CONFIG);
topLevelStyleModifications() {
const vc = this.visibleComponents;
if (!vc.footer && !vc.pageTitle) {
return '--footer-height: 1rem;';
} else if (!vc.footer) {
return '--footer-height: 5rem;';
} else if (!vc.pageTitle) {
return '--footer-height: 4rem;';
}
return '';
},
},
methods: {
/* Injects the users custom CSS as a style tag */
@ -135,7 +147,8 @@ export default {
},
},
/* Basic initialization tasks on app load */
mounted() {
async mounted() {
await this.$store.dispatch(Keys.INITIALIZE_CONFIG); // Initialize config before moving on
this.applyLanguage(); // Apply users local language
this.hideSplash(); // Hide the splash screen, if visible
if (this.appConfig.customCss) { // Inject users custom CSS, if present

View File

@ -48,7 +48,7 @@
"change-language-button": "Change App Language",
"reset-settings-button": "Reset Local Settings",
"app-info-button": "App Info",
"backup-note": "It is recommend to make a backup of your configuration before making changes.",
"backup-note": "It is recommended to make a backup of your configuration before making changes.",
"reset-config-msg-l1": "This will remove all user settings from local storage, but won't effect your 'conf.yml' file.",
"reset-config-msg-l2": "You should first backup any changes you've made locally, if you want to use them in the future.",
"reset-config-msg-l3": "Are you sure you want to proceed?",
@ -204,7 +204,7 @@
"start-editing-tooltip": "Enter the Interactive Editor",
"edit-site-data-subheading": "Edit Site Data",
"edit-page-info-btn": "Edit Page Info",
"edit-page-info-tooltip": "App title, description, nav links, footer text, etc",
"edit-page-info-tooltip": "App title, description, nav links, footer text, etc.",
"edit-app-config-btn": "Edit App Config",
"edit-app-config-tooltip": "All other app configuration options",
"config-save-methods-subheading": "Config Saving Options",

View File

@ -1,7 +1,8 @@
{
"home": {
"no-results": "nessun risultato trovato",
"no-data": "Nessun dato configurato"
"no-results": "Nessun risultato trovato",
"no-data": "Nessun dato configurato",
"no-items-section": "Nessun elemento"
},
"search": {
"search-label": "Ricerca",
@ -12,24 +13,25 @@
"login": {
"title": "Dashy",
"username-label": "Nome utente",
"password-label": "Parola d'ordine",
"password-label": "Password",
"login-button": "Login",
"remember-me-label": "Ricordami per",
"remember-me-never": "Mai",
"remember-me-hour": "4 ore",
"remember-me-day": "1 giorno",
"remember-me-week": "1 settimana",
"remember-me-long-time": "Molto tempo",
"error-missing-username": "Nome utente mancante",
"error-missing-password": "Password mancante",
"error-incorrect-username": "Utente non trovato",
"error-incorrect-password": "Password errata",
"success-message": "Entrando...",
"success-message": "Login in corso...",
"logout-message": "Disconnesso",
"already-logged-in-title": "Ha già effettuato il login",
"already-logged-in-text": "Hai effettuato l'accesso come",
"proceed-to-dashboard": "Procedi alla dashboard",
"log-out-button": "Disconnettersi",
"proceed-guest-button": "Procedi come ospite"
"already-logged-in-text": "Hai effettuato il login come",
"proceed-to-dashboard": "Vai alla dashboard",
"log-out-button": "Disconnessione",
"proceed-guest-button": "Procedi come Ospite"
},
"config": {
"main-tab": "Menu principale",
@ -42,12 +44,12 @@
"edit-css-button": "Modifica CSS personalizzato",
"cloud-sync-button": "Abilita sincronizzazione cloud",
"edit-cloud-sync-button": "Modifica sincronizzazione cloud",
"rebuild-app-button": "Ricostruisci applicazione",
"rebuild-app-button": "Ricompila l'applicazione",
"change-language-button": "Cambia la lingua dell'app",
"reset-settings-button": "Ripristina impostazioni locali",
"app-info-button": "Informazioni sull'app",
"backup-note": "Si consiglia di eseguire un backup della configurazione prima di apportare modifiche.",
"reset-config-msg-l1": "Questo rimuoverà tutte le impostazioni utente dalla memoria locale, ma non influenzerà il tuo file 'conf.yml'.",
"reset-config-msg-l1": "Questo rimuoverà tutte le impostazioni utente dalla memoria locale, ma non modificherà il tuo file 'conf.yml'.",
"reset-config-msg-l2": "Dovresti prima eseguire il backup di tutte le modifiche apportate localmente, se desideri utilizzarle in futuro.",
"reset-config-msg-l3": "Sei sicuro di voler procedere?",
"data-cleared-msg": "Dati cancellati con successo",
@ -58,14 +60,14 @@
"css-save-btn": "Salvare le modifiche",
"css-note-label": "Nota",
"css-note-l1": "Sarà necessario aggiornare la pagina affinché le modifiche abbiano effetto.",
"css-note-l2": "Le sostituzioni di stili sono memorizzate solo localmente, quindi si consiglia di fare una copia del proprio CSS.",
"css-note-l2": "Le personalizzazioni degli stili sono memorizzate solo localmente, quindi si consiglia di fare una copia del proprio CSS.",
"css-note-l3": "Per rimuovere tutti gli stili personalizzati, elimina i contenuti e premi Salva modifiche"
},
"alternate-views": {
"alternate-view-heading": "Cambia vista",
"default": "Predefinito",
"default": "Predefinita",
"workspace": "Area di lavoro",
"minimal": "Minimo"
"minimal": "Minima"
},
"settings": {
"theme-label": "Tema",
@ -73,22 +75,22 @@
"layout-auto": "Auto",
"layout-horizontal": "Orizzontale",
"layout-vertical": "Verticale",
"item-size-label": "Dimensione articolo",
"item-size-label": "Dimensione Elemento",
"item-size-small": "Piccolo",
"item-size-medium": "medio",
"item-size-medium": "Medio",
"item-size-large": "Grande",
"config-launcher-label": "config",
"config-launcher-tooltip": "Aggiorna configurazione",
"config-launcher-label": "Configurazione",
"config-launcher-tooltip": "Aggiorna Configurazione",
"sign-out-tooltip": "Disconnessione",
"sign-in-tooltip": "Accesso",
"sign-in-welcome": "Ciao {nome utente}!"
"sign-in-welcome": "Ciao {username}!"
},
"updates": {
"app-version-note": "Versione Dash",
"up-to-date": "Aggiornato",
"app-version-note": "Versione Dashy",
"up-to-date": "Aggiornata",
"out-of-date": "Aggiornamento disponibile",
"unsupported-version-l1": "Stai utilizzando una versione non supportata di Dashy",
"unsupported-version-l2": "Per la migliore esperienza e le patch di sicurezza recenti, aggiorna a"
"unsupported-version-l2": "Per un miglior utilizzo e una maggior sicurezza, si consiglia l'aggiornamento alla versione"
},
"language-switcher": {
"title": "Cambia lingua applicazione",
@ -97,52 +99,54 @@
"success-msg": "Lingua aggiornata a"
},
"theme-maker": {
"title": "Configuratore di temi",
"title": "Configurazione dei Temi",
"export-button": "Esporta variabili personalizzate",
"reset-button": "Ripristina stili per",
"reset-button": "Reset degli Stili per",
"show-all-button": "Mostra tutte le variabili",
"change-fonts-button": "Change Fonts",
"save-button": "Salva",
"cancel-button": "Annulla",
"saved-toast": "{theme} aggiornato con successo",
"copied-toast": "Dati del tema per {theme} copiati negli appunti",
"reset-toast": "Colori personalizzati per {theme} rimossi"
"copied-toast": "Dati del tema {theme} copiati negli appunti",
"reset-toast": "Rimossi Colori personalizzati per {theme}"
},
"config-editor": {
"save-location-label": "Salva l'indirizzo",
"location-local-label": "Applicare localmente",
"location-disk-label": "Scrivi modifiche al file di configurazione",
"save-location-label": "Posizione del salvataggio",
"location-local-label": "Salvataggio Locale",
"location-disk-label": "Salva le modifiche nel file di configurazione",
"save-button": "Salvare le modifiche",
"preview-button": "Preview Changes",
"valid-label": "La configurazione è valida",
"status-success-msg": "Compito completato",
"status-success-msg": "Attività completata",
"status-fail-msg": "Attività fallita",
"success-msg-disk": "File di configurazione scritto su disco con successo",
"success-msg-local": "Modifiche locali salvate con successo",
"success-note-l1": "L'app dovrebbe ricostruirsi automaticamente.",
"success-msg-disk": "File di configurazione scritto correttamente su disco",
"success-msg-local": "Modifiche locali salvate correttamente",
"success-note-l1": "L'app dovrebbe ricompilarsi automaticamente.",
"success-note-l2": "Questa operazione potrebbe richiedere fino a un minuto.",
"success-note-l3": "Sarà necessario aggiornare la pagina affinché le modifiche abbiano effetto.",
"error-msg-save-mode": "Seleziona una modalità di salvataggio: Locale o File",
"error-msg-save-mode": "Seleziona una modalità di salvataggio: Localmente o in un File",
"error-msg-cannot-save": "Si è verificato un errore durante il salvataggio della configurazione",
"error-msg-bad-json": "Errore in JSON, probabilmente non valido",
"error-msg-bad-json": "Errore nella struttura JSON, probabilmente non formattata correttamente",
"warning-msg-validation": "Avviso di convalida",
"not-admin-note": "Non puoi scrivere le modifiche su disco, perché non sei loggato come amministratore"
"not-admin-note": "Non puoi scrivere le modifiche su disco, perché non sei autenticato come amministratore"
},
"app-rebuild": {
"title": "Ricostruisci applicazione",
"rebuild-note-l1": "È necessaria una ricostruzione affinché le modifiche scritte nel file conf.yml abbiano effetto.",
"rebuild-note-l2": "Questo dovrebbe accadere automaticamente, ma in caso contrario, puoi attivarlo manualmente qui.",
"title": "Ricompila l'applicazione",
"rebuild-note-l1": "È necessaria una ricompilazione affinché le modifiche scritte nel file conf.yml abbiano effetto.",
"rebuild-note-l2": "La ricompilazione dovrebbe avvenire automaticamente, in caso contrario, può essere avviata manualmente.",
"rebuild-note-l3": "Questo non è richiesto per le modifiche memorizzate localmente.",
"rebuild-button": "Inizia a costruire",
"rebuilding-status-1": "Costruzione...",
"rebuild-button": "Inizia la ricompilazione",
"rebuilding-status-1": "Ricompilazione...",
"rebuilding-status-2": "Questo potrebbe richiedere alcuni minuti",
"error-permission": "Non hai l'autorizzazione per attivare questa azione",
"error-permission": "Non hai l'autorizzazione per avviare questa azione",
"success-msg": "Build completata con successo",
"fail-msg": "Operazione di compilazione non riuscita",
"reload-note": ora necessario ricaricare la pagina affinché le modifiche abbiano effetto",
"fail-msg": "Operazione di ricompilazione non riuscita",
"reload-note": necessario ricaricare la pagina affinché le modifiche abbiano effetto",
"reload-button": "Ricarica la pagina"
},
"cloud-sync": {
"title": "Backup e ripristino su cloud",
"intro-l1": "Il backup e il ripristino su cloud sono una funzionalità opzionale che ti consente di caricare la tua configurazione su Internet e quindi ripristinarla su qualsiasi altro dispositivo o istanza di Dashy.",
"title": "Backup e Restore su Cloud",
"intro-l1": "Il backup e il restore su cloud consentono di salvare la tua configurazione su un server internet e quindi ripristinarla su qualsiasi altro dispositivo o istanza Dashy.",
"intro-l2": "Tutti i dati sono completamente crittografati end-to-end con AES, utilizzando la tua password come chiave.",
"intro-l3": "Per maggiori informazioni, vedere il",
"backup-title-setup": "Fai un backup",
@ -152,11 +156,11 @@
"backup-button-setup": "Backup",
"backup-button-update": "Aggiorna backup",
"backup-id-label": "Il tuo ID di backup",
"backup-id-note": "Questo viene utilizzato per ripristinare dai backup in un secondo momento. Quindi tienilo, insieme alla tua password, in un posto sicuro.",
"backup-id-note": "ID utilizzato per il restore dal cloud. Conservarlo, insieme alla password, in un posto sicuro.",
"restore-title": "Ripristina un backup",
"restore-id-label": "Ripristina ID",
"restore-password-label": "Parola d'ordine",
"restore-button": "Ristabilire",
"restore-password-label": "Password",
"restore-button": "Restore",
"backup-missing-password": "Password mancante",
"backup-error-unknown": "Impossibile elaborare la richiesta",
"backup-error-password": "Password errata. Inserisci la tua password attuale.",
@ -164,9 +168,128 @@
"restore-success-msg": "Configurazione ripristinata con successo"
},
"menu": {
"sametab": "Apri nella scheda corrente",
"newtab": "Apri in una nuova scheda",
"modal": "Apri in modalità pop-up",
"workspace": "Apri nella vista dell'area di lavoro"
"open-section-title": "Open In",
"sametab": "Tab Attuale",
"newtab": "Nuovo Tab",
"modal": "Pop-Up",
"workspace": "Workspace",
"options-section-title": "Opzioni",
"edit-item": "Edita",
"move-item": "Copia/Sposta",
"remove-item": "Rimuovi"
},
"context-menus": {
"item": {
"open-section-title": "Apri In",
"sametab": "Tab Attuale",
"newtab": "Nuovo Tab",
"modal": "Pop-Up",
"workspace": "Workspace",
"clipboard": "Copia negli Appunti",
"options-section-title": "Opzioni",
"edit-item": "Edita",
"move-item": "Copia/Sposta",
"remove-item": "Rimuovi",
"copied-toast": "URL copiata negli appunti"
},
"section": {
"open-section": "Apri Sezione",
"edit-section": "Edita",
"move-section": "Sposta in",
"remove-section": "Rimuovi"
}
},
"interactive-editor": {
"menu": {
"start-editing-tooltip": "Apri l'Editor Interattivo",
"edit-site-data-subheading": "Modifica i dati del sito",
"edit-page-info-btn": "Modifica le informazioni della pagina",
"edit-page-info-tooltip": "Titolo App, descrizione, link navigazione, piè di pagina, etc.",
"edit-app-config-btn": "Modifica Configurazione App",
"edit-app-config-tooltip": "Altre opzioni di configurazione",
"config-save-methods-subheading": "Opzioni di salvataggio della configurazione",
"save-locally-btn": "Salva Localmente",
"save-locally-tooltip": "Save localmente, nell'area dati del browser. Il file di configurazione non verrà modificato, le modifiche sono salvate solo su questo dispositivo",
"save-disk-btn": "Salva su Disco",
"save-disk-tooltip": "Salva la configurazione su disco, nel file conf.yml. Verrà effettuato un backup, la configurazione attuale verrà sovrascritta",
"export-config-btn": "Esporta la Configurazione",
"export-config-tooltip": "Visualizza ed esporta la configurazione, in un file o negli appunti",
"cloud-backup-btn": "Backup nel Cloud",
"cloud-backup-tooltip": "Salva il backup criptato della configurazione nel cloud",
"edit-raw-config-btn": "Modifica la configurazione nativa",
"edit-raw-config-tooltip": "Visualizza e modifica la configurazione nativa con l'editor JSON",
"cancel-changes-btn": "Annulla le Modifiche",
"cancel-changes-tooltip": "Annulla le modifiche ed esci dalla modalità modifica. La configurazione attuale non verrà modificata",
"edit-mode-name": "Modalità Modifica",
"edit-mode-subtitle": "Sei in modalità modifica",
"edit-mode-description": "Puoi modificare la configurazione e verificare il risultato, ma prima del salvataggio, nessun cambiamento verrà preservato",
"save-stage-btn": "Salva",
"cancel-stage-btn": "Annulla"
},
"edit-item": {
"missing-title-err": "Il titolo è obbligatorio"
},
"edit-section": {
"edit-section-title": "Modifica Sezione",
"add-section-title": "Aggiungi Nuova Sezione",
"edit-tooltip": "Seleziona per modificare, tasto destro per altre opzioni",
"remove-confirm": "Sei sicuro di voler rimuovere questa sezione? Questa scelta può essere rivista successivamente."
},
"edit-app-config": {
"warning-msg-title": "Procedi con cautela",
"warning-msg-l1": "Queste opzioni sono necessarie per la configurazione avanzata dell'app",
"warning-msg-l2": "Se non sei sicuro di qualche opzione, controlla la",
"warning-msg-docs": "documentazione",
"warning-msg-l3": "per evitare spiacevoli conseguenze."
},
"export": {
"export-title": "Esporta Configurazione",
"copy-clipboard-btn": "Copia negli Appunti",
"copy-clipboard-tooltip": "Copia la configurazione negli appunti, in formato YAML",
"download-file-btn": "Salva file",
"download-file-tooltip": "Salva la configurazione dell'app in un file YAML",
"view-title": "View Config"
}
},
"widgets": {
"general": {
"loading": "Caricamento...",
"show-more": "Dettagli",
"show-less": "Minori dettagli",
"open-link": "Continua Lettura"
},
"pi-hole": {
"status-heading": "Stato"
},
"stat-ping": {
"up": "Online",
"down": "Offline"
},
"net-data": {
"cpu-chart-title": "Utilizzo CPU",
"mem-chart-title": "Utilizzo Memoria",
"mem-breakdown-title": "Dettaglio Utilizzo Memoria",
"load-chart-title": "Carico del Sistema"
},
"glances": {
"disk-space-free": "Utilizzabile",
"disk-space-used": "Utilizzato",
"disk-mount-point": "Mount Point",
"disk-file-system": "File System",
"disk-io-read": "Lettura",
"disk-io-write": "Scrittura",
"system-load-desc": "Numero di processi in attesa nella coda di esecuzione, calcolati sulla media di tutti i core"
},
"system-info": {
"uptime": "Uptime"
},
"flight-data": {
"arrivals": "Arrivi",
"departures": "Partenze"
},
"tfl-status": {
"good-service-all": "Buon servizio su tutte le Linee",
"good-service-rest": "Buon servizio su tutte le altre Linee"
}
}
}
}

View File

@ -231,8 +231,8 @@ export default {
const newItem = item;
newItem.id = this.itemId;
if (newItem.hotkey) newItem.hotkey = parseInt(newItem.hotkey, 10);
const strToTags = (str) => {
const tagArr = str.split(',');
const strToTags = (tags) => {
const tagArr = (typeof tags === 'string') ? tags.split(',') : tags;
return tagArr.map((tag) => tag.trim().toLowerCase().replace(/[^a-z0-9]+/, ''));
};
const strToBool = (str) => {

View File

@ -17,6 +17,8 @@
<h3>{{ title }}</h3>
<EditModeIcon v-if="isEditMode" @click="openEditModal"
v-tooltip="editTooltip()" class="edit-mode-item" />
<OpenIcon @click.prevent.stop="openContextMenu" @contextmenu.prevent
class="edit-mode-item" />
</label>
<div class="collapsible-content">
<div class="content-inner">
@ -31,6 +33,7 @@
import { localStorageKeys } from '@/utils/defaults';
import Icon from '@/components/LinkItems/ItemIcon.vue';
import EditModeIcon from '@/assets/interface-icons/interactive-editor-edit-mode.svg';
import OpenIcon from '@/assets/interface-icons/config-open-settings.svg';
export default {
name: 'CollapsableContainer',
@ -48,6 +51,7 @@ export default {
components: {
Icon,
EditModeIcon,
OpenIcon,
},
computed: {
isEditMode() {
@ -244,6 +248,8 @@ export default {
float: right;
right: 0.5rem;
top: 0.5rem;
margin-left: 0.2rem;
margin-right: 0.2rem;
}
/* Makes sections fill available space */

View File

@ -3,7 +3,7 @@
classes="dashy-modal">
<div slot="top-right" @click="hide()">Close</div>
<a @click="hide()" class="close-button" title="Close">x</a>
<iframe v-if="url" :src="url" @keydown.esc="close" class="frame"/>
<iframe v-if="url" :src="url" @keydown.esc="close" class="frame" allow="fullscreen" />
<div v-else class="no-url">No URL Specified</div>
</modal>
</template>

View File

@ -1,9 +1,9 @@
<template ref="container">
<div :class="`item-wrapper wrap-size-${itemSize}`">
<a @click="beforeLaunchItem"
<a @click="itemClicked"
@mouseup.right="openContextMenu"
@contextmenu.prevent
:href="hyperLinkHref"
:href="url"
:target="anchorTarget"
:class="`item ${makeClassList}`"
v-tooltip="getTooltipOptions()"
@ -91,6 +91,7 @@ export default {
statusCheckInterval: Number, // Num seconds beteween repeating checks
statusCheckAllowInsecure: Boolean, // Status check ignore SSL certs
statusCheckAcceptCodes: String, // Allow status checks to pass with a code other than 200
statusCheckMaxRedirects: Number, // Specify max number of redirects
parentSectionTitle: String, // Title of parent section (for add new)
isAddNew: Boolean, // Only set if 'fake' item used as Add New button
},
@ -136,30 +137,6 @@ export default {
};
},
methods: {
/* Called when an item is clicked, manages the opening of modal & resets the search field */
beforeLaunchItem(e) {
if (this.isEditMode) { // If in edit mode, open settings, don't launch app
this.openItemSettings();
return;
}
if (e.altKey) {
e.preventDefault();
this.launchItem('modal');
} else if (this.accumulatedTarget === 'modal') {
this.launchItem('modal');
} else if (this.accumulatedTarget === 'workspace') {
this.launchItem('workspace');
} else if (this.accumulatedTarget === 'clipboard') {
this.launchItem('clipboard');
}
// Clear search bar
this.$emit('itemClicked');
// Update the most/ last used ledger, for smart-sorting
if (!this.appConfig.disableSmartSort) {
this.incrementMostUsedCount(this.id);
this.incrementLastUsedCount(this.id);
}
},
/* Returns configuration object for the tooltip */
getTooltipOptions() {
if (!this.description && !this.provider) return {}; // If no description, then skip
@ -179,7 +156,6 @@ export default {
classes: `item-description-tooltip tooltip-is-${this.itemSize}`,
};
},
/* Open the Edit Item modal form */
openItemSettings() {
this.editMenuOpen = true;
this.contextMenuOpen = false;

View File

@ -45,11 +45,11 @@ export default {
return this.$store.getters.appConfig;
},
/* Determines the type of icon */
iconType: function iconType() {
iconType() {
return this.determineImageType(this.icon);
},
/* Gets the icon path, dependent on icon type */
iconPath: function iconPath() {
iconPath() {
if (this.broken) return this.getFallbackIcon();
return this.getIconPath(this.icon, this.url);
},
@ -176,7 +176,7 @@ export default {
},
/* Fetches the path of local images, from Docker container */
getLocalImagePath(img) {
return `${iconCdns.localPath}/${img}`;
return `/${iconCdns.localPath}/${img}`;
},
/* Formats the URL for fetching the generative icons */
getGenerativeIcon(url, cdn) {

View File

@ -54,6 +54,7 @@
:statusCheckInterval="statusCheckInterval"
:statusCheckAllowInsecure="item.statusCheckAllowInsecure"
:statusCheckAcceptCodes="item.statusCheckAcceptCodes"
:statusCheckMaxRedirects="item.statusCheckMaxRedirects"
@itemClicked="$emit('itemClicked')"
@triggerModal="triggerModal"
:isAddNew="false"
@ -166,7 +167,7 @@ export default {
return this.$store.state.editMode;
},
itemSize() {
return this.$store.getters.iconSize;
return this.displayData.itemSize || this.$store.getters.iconSize;
},
sortOrder() {
return this.displayData.sortBy || defaultSortOrder;

View File

@ -1,7 +1,7 @@
<template>
<transition name="slide">
<div class="context-menu" v-if="show && !isMenuDisabled"
:style="posX && posY ? `top:${posY}px;left:${posX}px;` : ''">
:style="posX && posY ? calcPosition() : ''">
<!-- Open Options -->
<ul class="menu-section">
<li @click="openSection()">
@ -59,6 +59,13 @@ export default {
removeSection() {
this.$emit('removeSection');
},
calcPosition() {
const bounds = this.$parent.$el.getBoundingClientRect();
const left = this.posX < (bounds.right + bounds.left) / 2;
const position = `top:${this.posY}px;${left ? 'left' : 'right'}:\
${left ? this.posX : document.documentElement.clientWidth - this.posX}px;`;
return position;
},
},
};
</script>

View File

@ -19,6 +19,7 @@
:enableStatusCheck="shouldEnableStatusCheck(item.statusCheck)"
:statusCheckAllowInsecure="item.statusCheckAllowInsecure"
:statusCheckAcceptCodes="item.statusCheckAcceptCodes"
:statusCheckMaxRedirects="item.statusCheckMaxRedirects"
:statusCheckInterval="getStatusCheckInterval()"
@itemClicked="$emit('itemClicked')"
@triggerModal="triggerModal"

View File

@ -5,15 +5,23 @@
@click="navVisible = !navVisible"
/>
<nav id="nav" v-if="navVisible">
<router-link
v-for="(link, index) in links"
:key="index"
:to="link.path"
:href="link.path"
:target="isUrl(link.path) ? '_blank' : ''"
rel="noopener noreferrer"
class="nav-item"
>{{link.title}}</router-link>
<!-- Render either router-link or anchor, depending if internal / external link -->
<template v-for="(link, index) in links">
<router-link v-if="!isUrl(link.path)"
:key="index"
:to="link.path"
class="nav-item"
>{{link.title}}
</router-link>
<a v-else
:key="index"
:href="link.path"
:target="determineTarget(link)"
class="nav-item"
rel="noopener noreferrer"
>{{link.title}}
</a>
</template>
</nav>
</div>
</template>
@ -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;
}
},
},
};
</script>

View File

@ -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() {

View File

@ -1,6 +1,6 @@
<template>
<div class="web-content" :id="id">
<iframe :src="url" />
<iframe :src="url" allow="fullscreen" />
</div>
</template>

View File

@ -14,8 +14,7 @@ export default {
credentials() {
if (this.options.username && this.options.password) {
const stringifiedUser = `${this.options.username}:${this.options.password}`;
const headers = { Authorization: `Basic ${window.btoa(stringifiedUser)}` };
return { headers };
return { Authorization: `Basic ${window.btoa(stringifiedUser)}` };
}
return null;
},

View File

@ -106,8 +106,9 @@ const WidgetMixin = {
const CustomHeaders = options || null;
const headers = this.useProxy
? { 'Target-URL': endpoint, CustomHeaders: JSON.stringify(CustomHeaders) } : CustomHeaders;
const timeout = this.options.timeout || 500;
const requestConfig = {
method, url, headers, data,
method, url, headers, data, timeout,
};
// Make request
return new Promise((resolve, reject) => {

View File

@ -1,6 +1,8 @@
/* eslint-disable no-param-reassign, prefer-destructuring */
import Vue from 'vue';
import Vuex from 'vuex';
import axios from 'axios';
import yaml from 'js-yaml';
import Keys from '@/utils/StoreMutations';
import ConfigAccumulator from '@/utils/ConfigAccumalator';
import { componentVisibility } from '@/utils/ConfigHelpers';
@ -14,6 +16,7 @@ Vue.use(Vuex);
const {
INITIALIZE_CONFIG,
SET_CONFIG,
SET_REMOTE_CONFIG,
SET_MODAL_OPEN,
SET_LANGUAGE,
SET_ITEM_LAYOUT,
@ -38,6 +41,7 @@ const {
const store = new Vuex.Store({
state: {
config: {},
remoteConfig: {}, // The configuration stored on the server
editMode: false, // While true, the user can drag and edit items + sections
modalOpen: false, // KB shortcut functionality will be disabled when modal is open
navigateConfToTab: undefined, // Used to switch active tab in config modal
@ -126,6 +130,9 @@ const store = new Vuex.Store({
[SET_CONFIG](state, config) {
state.config = config;
},
[SET_REMOTE_CONFIG](state, config) {
state.remoteConfig = config;
},
[SET_LANGUAGE](state, lang) {
const newConfig = state.config;
newConfig.appConfig.language = lang;
@ -271,7 +278,9 @@ const store = new Vuex.Store({
},
actions: {
/* Called when app first loaded. Reads config and sets state */
[INITIALIZE_CONFIG]({ commit }) {
async [INITIALIZE_CONFIG]({ commit }) {
// Get the config file from the server and store it for use by the accumulator
commit(SET_REMOTE_CONFIG, yaml.load((await axios.get('conf.yml')).data));
const deepCopy = (json) => JSON.parse(JSON.stringify(json));
const config = deepCopy(new ConfigAccumulator().config());
commit(SET_CONFIG, config);

View File

@ -14,30 +14,34 @@ import {
} from '@/utils/defaults';
import ErrorHandler from '@/utils/ErrorHandler';
import { applyItemId } from '@/utils/SectionHelpers';
import conf from '../../public/conf.yml';
import $store from '@/store';
import buildConf from '../../public/conf.yml';
export default class ConfigAccumulator {
constructor() {
this.conf = conf;
this.conf = $store.state.remoteConfig;
}
/* App Config */
appConfig() {
let appConfigFile = {};
// Set app config from file
if (this.conf) appConfigFile = this.conf.appConfig || {};
if (this.conf) appConfigFile = this.conf.appConfig || buildConf.appConfig || {};
// Fill in defaults if anything missing
let usersAppConfig = defaultAppConfig;
if (localStorage[localStorageKeys.APP_CONFIG]) {
usersAppConfig = JSON.parse(localStorage[localStorageKeys.APP_CONFIG]);
} else if (appConfigFile !== {}) {
} else if (Object.keys(appConfigFile).length > 0) {
usersAppConfig = appConfigFile;
}
// Some settings have their own local storage keys, apply them here
usersAppConfig.layout = localStorage[localStorageKeys.LAYOUT_ORIENTATION]
|| appConfigFile.layout || defaultLayout;
usersAppConfig.iconSize = localStorage[localStorageKeys.ICON_SIZE]
|| appConfigFile.iconSize || defaultIconSize;
usersAppConfig.layout = appConfigFile.layout
|| localStorage[localStorageKeys.LAYOUT_ORIENTATION]
|| defaultLayout;
usersAppConfig.iconSize = appConfigFile.iconSize
|| localStorage[localStorageKeys.ICON_SIZE]
|| defaultIconSize;
// Don't let users modify users locally
if (appConfigFile.auth) usersAppConfig.auth = appConfigFile.auth;
// All done, return final appConfig object

View File

@ -36,6 +36,18 @@
},
"path": {
"type": "string"
},
"target": {
"title": "Opening Method",
"type": "string",
"enum": [
"newtab",
"sametab",
"parent",
"top"
],
"default": "newtab",
"description": "Where / how the item is opened when it's clicked"
}
}
}
@ -789,11 +801,16 @@
"description": "Allows for running status checks on insecure content/ non-HTTPS apps. Prevents checks failing for non-SSL sites"
},
"statusCheckAcceptCodes": {
"title": "Accepted HTTP Status Codes",
"title": "Status Check - Accepted HTTP Codes",
"type": "string",
"default": "",
"description": "If your service's response code is anything other than 2xx, then you can opt to specify an alternative success code"
},
"statusCheckMaxRedirects": {
"title": "Status Check - Max Redirects",
"type": "number",
"default": "0",
"description": "If your service redirects to another page, and you would like status checks to follow redirects, then specify the maximum number of redirects here"
},
"color": {
"title": "Custom Color",
"type": "string",
@ -820,6 +837,21 @@
"type": "string",
"description": "The type of widget to use, see docs for supported options"
},
"updateInterval": {
"title": "Update Interval",
"type": "number",
"description": "Specified in seconds. If set, widget data will be re-retched at this interval to display up-to-date data"
},
"timeout": {
"title": "Timeout",
"type": "number",
"description": "Specified in milliseconds. If set, request will timeout after the specified interval. Defaults to 500/ half a sec"
},
"useProxy": {
"title": "Use Proxy?",
"type": "boolean",
"description": "If set to true, request will be proxied through the backend. Requires the Node server to be running"
},
"options": {
"title": "Widget Options",
"type": "object",

View File

@ -1,7 +1,8 @@
import axios from 'axios';
import yaml from 'js-yaml';
import { register } from 'register-service-worker';
import { sessionStorageKeys } from '@/utils/defaults';
import { statusMsg, statusErrorMsg } from '@/utils/CoolConsole';
import conf from '../../public/conf.yml';
/* Sets a local storage item with the state from the SW lifecycle */
const setSwStatus = (swStateToSet) => {
@ -31,7 +32,8 @@ const setSwStatus = (swStateToSet) => {
* Disable if not running in production
* Or disable if user specified to disable
*/
const shouldEnableServiceWorker = () => {
const shouldEnableServiceWorker = async () => {
const conf = yaml.load((await axios.get('conf.yml')).data);
if (conf && conf.appConfig && conf.appConfig.enableServiceWorker) {
setSwStatus({ disabledByUser: false });
return true;
@ -51,8 +53,8 @@ const printSwStatus = (msg) => {
const swUrl = `${process.env.BASE_URL || '/'}service-worker.js`;
/* If service worker enabled, then register it, and print message when status changes */
const registerServiceWorker = () => {
if (shouldEnableServiceWorker()) {
const registerServiceWorker = async () => {
if (await shouldEnableServiceWorker()) {
register(swUrl, {
ready() {
setSwStatus({ ready: true });

View File

@ -14,7 +14,7 @@ class KeycloakAuth {
const { auth } = getAppConfig();
const { serverUrl, realm, clientId } = auth.keycloak;
const initOptions = {
url: `${serverUrl}/auth`, realm, clientId, onLoad: 'login-required',
url: `${serverUrl}`, realm, clientId, onLoad: 'login-required',
};
this.keycloakClient = Keycloak(initOptions);

View File

@ -2,6 +2,7 @@
const KEY_NAMES = [
'INITIALIZE_CONFIG',
'SET_CONFIG',
'SET_REMOTE_CONFIG',
'SET_MODAL_OPEN',
'SET_LANGUAGE',
'SET_EDIT_MODE',

View File

@ -32,6 +32,7 @@ export const LoadExternalTheme = function th() {
const preloadTheme = (href) => {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = href;
document.head.appendChild(link);
return new Promise((resolve, reject) => {

View File

@ -201,7 +201,7 @@ module.exports = {
generativeFallback: 'https://evatar.io/{icon}',
localPath: './item-icons',
faviconName: 'favicon.ico',
homeLabIcons: 'https://raw.githubusercontent.com/WalkxCode/dashboard-icons/master/png/{icon}.png',
homeLabIcons: 'https://raw.githubusercontent.com/walkxhub/dashboard-icons/master/png/{icon}.png',
homeLabIconsFallback: 'https://raw.githubusercontent.com/NX211/homer-icons/master/png/{icon}.png',
},
/* API endpoints for widgets that need to fetch external data */

View File

@ -19,7 +19,7 @@
</router-link>
</div>
<!-- Main content, section for each group of items -->
<div v-if="checkTheresData(sections)"
<div v-if="checkTheresData(sections) || isEditMode"
:class="`item-group-container `
+ `orientation-${layout} `
+ `item-size-${itemSizeBound} `
@ -50,7 +50,7 @@
<AddNewSection v-if="isEditMode" />
</div>
<!-- Show message when there's no data to show -->
<div v-if="checkIfResults()" class="no-data">
<div v-if="checkIfResults() && !isEditMode" class="no-data">
{{searchValue ? $t('home.no-results') : $t('home.no-data')}}
</div>
<!-- Show banner at bottom of screen, for Saving config changes -->
@ -213,7 +213,7 @@ export default {
overflow: auto;
@extend .scroll-bar;
@include monitor-up {
max-width: 1400px;
max-width: 85%;
}
/* Options for alternate layouts, triggered by buttons */

View File

@ -1169,6 +1169,15 @@
"@sentry/utils" "6.17.4"
tslib "^1.9.3"
"@sentry/hub@6.18.1":
version "6.18.1"
resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-6.18.1.tgz#fcfb8cb84515efefaf4e48472305ea5a71455abb"
integrity sha512-+zGzgc/xX3an/nKA3ELMn9YD9VmqbNaNwWZ5/SjNUvzsYHh2UNZ7YzT8WawQsRVOXLljyCKxkWpFB4EchiYGbw==
dependencies:
"@sentry/types" "6.18.1"
"@sentry/utils" "6.18.1"
tslib "^1.9.3"
"@sentry/minimal@6.17.4":
version "6.17.4"
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.17.4.tgz#6a35dbdb22a1c532d1eb7b4c0d9223618cb67ccd"
@ -1178,15 +1187,24 @@
"@sentry/types" "6.17.4"
tslib "^1.9.3"
"@sentry/tracing@^6.17.4":
version "6.17.4"
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.17.4.tgz#17c2ab50d9e4cdf727b9b25e7f91ae3a9ea19437"
integrity sha512-UV6wWH/fqndts0k0cptsNtzD0h8KXqHInJSCGqlWDlygFRO16jwMKv0wfXgqsgc3cBGDlsl8C4l6COSwz9ROdg==
"@sentry/minimal@6.18.1":
version "6.18.1"
resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-6.18.1.tgz#eac73d2262589930aa0bb33e0e12380ac5b766a9"
integrity sha512-dm+0MuasWNi/LASvHX+09oCo8IBZY5WpMK8qXvQMnwQ9FVfklrjcfEI3666WORDCmeUhDCSeL2MbjPDm+AmPLg==
dependencies:
"@sentry/hub" "6.17.4"
"@sentry/minimal" "6.17.4"
"@sentry/types" "6.17.4"
"@sentry/utils" "6.17.4"
"@sentry/hub" "6.18.1"
"@sentry/types" "6.18.1"
tslib "^1.9.3"
"@sentry/tracing@^6.17.5":
version "6.18.1"
resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-6.18.1.tgz#7cc54b328dd051102900ade53e907e7441426f83"
integrity sha512-OxozmSfxGx246Ae1XhO01I7ZWxO3briwMBh55E5KyjQb8fuS9gVE7Uy8ZRs5hhNjDutFAU7nMtC0zipfVxP6fg==
dependencies:
"@sentry/hub" "6.18.1"
"@sentry/minimal" "6.18.1"
"@sentry/types" "6.18.1"
"@sentry/utils" "6.18.1"
tslib "^1.9.3"
"@sentry/types@6.17.4":
@ -1194,6 +1212,11 @@
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.17.4.tgz#36b78d7c4a6de19b2bbc631bb34893bcad30c0ba"
integrity sha512-RUyiXCKf61k2GIMP7FQX0naoSew4zLxe+UrtbjwVcWU4AFPZfH7tLNtTpVE85zAKbxsaiq3OD2FPtTZarHcwxQ==
"@sentry/types@6.18.1":
version "6.18.1"
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.18.1.tgz#e2de38dd0da8096a5d22f8effc6756c919266ede"
integrity sha512-wp741NoBKnXE/4T9L723sWJ8EcNMxeTIT1smgNJOfbPwrsDICoYmGEt6JFa05XHpWBGI66WuNvnDjoHVeh6zhA==
"@sentry/utils@6.17.4":
version "6.17.4"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.17.4.tgz#4f109629d2e7f16c5595b4367445ef47bfe96b61"
@ -1202,6 +1225,14 @@
"@sentry/types" "6.17.4"
tslib "^1.9.3"
"@sentry/utils@6.18.1":
version "6.18.1"
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-6.18.1.tgz#1aa819502b042540612f4db7bcb86c7b176f5a6b"
integrity sha512-IFZmuvA+c5lDGlZEri13JSyUP0BHelzY0S4dcKxAzskPW+BtBdQDgYGV90iED1y+IRMLawWb34GF7HyJSouN1Q==
dependencies:
"@sentry/types" "6.18.1"
tslib "^1.9.3"
"@sentry/vue@^6.17.4":
version "6.17.4"
resolved "https://registry.yarnpkg.com/@sentry/vue/-/vue-6.17.4.tgz#f77ad22fcc6b9d2efa5967d133dccc24d49163a8"
@ -8715,10 +8746,10 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
simple-icons@^6.8.0:
version "6.8.0"
resolved "https://registry.yarnpkg.com/simple-icons/-/simple-icons-6.8.0.tgz#ee837f4222dc00b600d90ec0025f52ea44c2c749"
integrity sha512-Ow1ISgCpRZuXa1xvwVqUrep6xqdtNmcgqd9pCtMgni7m1XmQI56EuSSJYAbyvCcoakXVcbtHFajtPXEd86iMCA==
simple-icons@^6.9.0:
version "6.12.0"
resolved "https://registry.yarnpkg.com/simple-icons/-/simple-icons-6.12.0.tgz#92bf40b332d759d2fdd7f59929e00c90e921e7c8"
integrity sha512-SLupP1dHUcrLSDHpOfjy4u010IYj79QzFkH460ITfGElvo9cB1V2hO4krz0aSZpRWFglwtFJIYPaIOKHp4+NBw==
simple-swizzle@^0.2.2:
version "0.2.2"