mirror of
https://github.com/Lissy93/dashy.git
synced 2024-11-22 11:42:25 +03:00
Merge branch 'FEAT/Dashy-V3' into FEATURE/environment-variables
This commit is contained in:
commit
f98441644b
@ -21,4 +21,4 @@ indent_size = 2
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
# Licensed under MIT, (C) 2021 Alicia Sykes <https://aliciasykes.com>
|
||||
# Licensed under MIT, (C) 2024 Alicia Sykes <https://aliciasykes.com>
|
17
.env
17
.env
@ -24,9 +24,24 @@
|
||||
# Defaults to true
|
||||
# REDIRECT_HTTPS=true
|
||||
|
||||
# The path to the user data directory
|
||||
# USER_DATA_DIR=user-data
|
||||
|
||||
# Override where the path to the configuration file is, can be a remote URL
|
||||
# VUE_APP_CONFIG_PATH=/conf.yml
|
||||
|
||||
# Usually the same as BASE_URL, but accessible in frontend
|
||||
# VUE_APP_DOMAIN=https://dashy.to
|
||||
|
||||
# Override the page title for the frontend app
|
||||
# VUE_APP_TITLE=''
|
||||
|
||||
# Set the default view to load on startup (can be `minimal`, `workspace` or `home`)
|
||||
# VUE_APP_STARTING_VIEW=home
|
||||
|
||||
# Set the Vue app routing mode (can be 'hash', 'history' or 'abstract')
|
||||
# VUE_APP_ROUTING_MODE=history
|
||||
|
||||
# Should enable SRI for build script and link resources
|
||||
# INTEGRITY=true
|
||||
|
||||
@ -37,7 +52,7 @@
|
||||
# VUE_APP_VERSION=2.0.0
|
||||
|
||||
# Directory for conf.yml backups
|
||||
# BACKUP_DIR=./public/
|
||||
# BACKUP_DIR=./user-data/
|
||||
|
||||
# Setup any other user defined vars by prepending VUE_APP_ to the var name
|
||||
# VUE_APP_pihole_ip=http://your.pihole.ip
|
||||
|
32
.github/AUTHORS.txt
vendored
32
.github/AUTHORS.txt
vendored
@ -2,6 +2,7 @@
|
||||
5idereal <nelson22768384@gmail.com> - 1 commits
|
||||
Alberto <3170731+albcp@users.noreply.github.com> - 1 commits
|
||||
Alicia <liss-bot@d0h.co> - 1 commits
|
||||
Aly Mohamed <alydemah@gmail.com> - 1 commits
|
||||
Artyom <32810520+AaronPorts@users.noreply.github.com> - 1 commits
|
||||
BRAVO68WEB <jbandyopadhayaya@gmail.com> - 1 commits
|
||||
Begin <support@begin.com> - 1 commits
|
||||
@ -9,6 +10,8 @@ Bhasher <github.it@bhasher.com> - 1 commits
|
||||
Bjorn Lammers <walkxnl@gmail.com> - 1 commits
|
||||
Bumsoo Kim <bskim45@gmail.com> - 1 commits
|
||||
BySempron <barquin_rkz@hotmail.com> - 1 commits
|
||||
Celestial.y <celestial.y@outlook.com> - 1 commits
|
||||
CrazyWolf13 <CrazyWolf13@users.noreply.github.com> - 1 commits
|
||||
David <skaarj1989@gmail.com> - 1 commits
|
||||
Dawid Pietrykowski <d.pietrykowski16@gmail.com> - 1 commits
|
||||
DeepSource Bot <bot@deepsource.io> - 1 commits
|
||||
@ -23,6 +26,7 @@ FedelloKirfed <fedellokirfed@proton.me> - 1 commits
|
||||
FormatToday <616099456@qq.com> - 1 commits
|
||||
Garrett Brown <gdbrown09@gmail.com> - 1 commits
|
||||
Harald Töpfer <62364169+hubortje@users.noreply.github.com> - 1 commits
|
||||
Hendrik Strydom <56320839+FraglyG@users.noreply.github.com> - 1 commits
|
||||
Ian Neal <wopr@wopr.tech> - 1 commits
|
||||
Iaroslav Dronskii <iaroslav@dronskii.ru> - 1 commits
|
||||
Ishan Jain <ishan@jain.se> - 1 commits
|
||||
@ -57,7 +61,6 @@ Smexhy <roman.bartik@icloud.com> - 1 commits
|
||||
Stavros Kois <47820033+stavros-k@users.noreply.github.com> - 1 commits
|
||||
Steffen Schmidt <service.github@nocer.net> - 1 commits
|
||||
Steven Kast <steven.m.kast@gmail.com> - 1 commits
|
||||
Tobias <96661824+CrazyWolf13@users.noreply.github.com> - 1 commits
|
||||
Xert <xertdev@gmail.com> - 1 commits
|
||||
allozavrr <razviyarrr@gmail.com> - 1 commits
|
||||
baifengheixi <98794233+baifengheixi@users.noreply.github.com> - 1 commits
|
||||
@ -71,6 +74,7 @@ jnach <33467747+jnach@users.noreply.github.com> - 1 commits
|
||||
jrobles98 <jose98_21@hotmail.com> - 1 commits
|
||||
kxenox <floriandel@msn.com> - 1 commits
|
||||
laker j.v <la@laker.gay> - 1 commits
|
||||
markxoe <mark@oude-elberink.de> - 1 commits
|
||||
pablomalo <paul.gouin50@gmail.com> - 1 commits
|
||||
poisonadder7 <github.com@loginz.uk> - 1 commits
|
||||
rtm516 <ryantmilner@hotmail.co.uk> - 1 commits
|
||||
@ -81,6 +85,7 @@ sur1v <gsuriv@gmail.com> - 1 commits
|
||||
tazboyz16 <tazboyz_16@yahoo.com> - 1 commits
|
||||
zcq100 <zcq100@gmail.com> - 1 commits
|
||||
Ángel Fernández Sánchez <angelfx19@gmail.com> - 1 commits
|
||||
Al Saleh <alayham@alayham.com> - 2 commits
|
||||
Alejandro Pinar Ruiz <37040888+pinarruiz@users.noreply.github.com> - 2 commits
|
||||
Alessandro Del Prete <alessandro.delprete@gmail.com> - 2 commits
|
||||
Alexander Mnich <56564725+a-mnich@users.noreply.github.com> - 2 commits
|
||||
@ -89,7 +94,9 @@ Brendan O'Leary <boleary@gitlab.com> - 2 commits
|
||||
CHAIYEON CHO <stoutcho21@gmail.com> - 2 commits
|
||||
Dan Gilbert <ddg@daentech.co.uk> - 2 commits
|
||||
Denis Savosin <dsavosin@at-consulting.ru> - 2 commits
|
||||
Mihai <31653632+z3r0l1nk@users.noreply.github.com> - 2 commits
|
||||
Ruben Silva <rubensilva945@gmail.com> - 2 commits
|
||||
Tammes Burghard <kde@kuchenmampfer.de> - 2 commits
|
||||
Thomas Dissert <dissertthomas@gmail.com> - 2 commits
|
||||
cauterize <cauterize@programmer.net> - 2 commits
|
||||
hockeymikey <hockeymikey@hockeymikey.com> - 2 commits
|
||||
@ -102,6 +109,7 @@ patrickheeney <patrickheeney@gmail.com> - 2 commits
|
||||
rokiden <cheto@bk.ru> - 2 commits
|
||||
root <root@dashy-dev.alayham.com> - 2 commits
|
||||
rtm516 <rtm516@users.noreply.github.com> - 2 commits
|
||||
z3r0l1nk <stroescu.mihai@gmail.com> - 2 commits
|
||||
zcq100 <m@zcq100.com> - 2 commits
|
||||
ᗪєνιη ᗷυнʟ <onedr0p@users.noreply.github.com> - 2 commits
|
||||
Alucarddelta <dreyer.brent@gmail.com> - 3 commits
|
||||
@ -120,7 +128,6 @@ Rémy GRANDIN <remy.gr@ndin.fr> - 4 commits
|
||||
Todd E Johnson <todd@toddejohnson.net> - 4 commits
|
||||
Totto16 <tobiausgais@gmail.com> - 4 commits
|
||||
Walkx <walkxnl@gmail.com> - 4 commits
|
||||
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> - 4 commits
|
||||
wozboz <philip@iuno.tech> - 4 commits
|
||||
Alberto Caravaca <3170731+albcp@users.noreply.github.com> - 5 commits
|
||||
Alicie <gh@d0h.co> - 5 commits
|
||||
@ -131,10 +138,15 @@ Erik Vroon <erik.vroon22@gmail.com> - 6 commits
|
||||
Jammo2k5 <jammo2k5@hotmail.co.uk> - 6 commits
|
||||
Leonardo Covarrubias <leos@leocov.com> - 6 commits
|
||||
Magnus Sundström <kmvsundstrom@gmail.com> - 6 commits
|
||||
dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> - 6 commits
|
||||
kristian <kristianbrasel@gmail.com> - 6 commits
|
||||
liss-bot <liss-bot@users.noreply.github.com> - 6 commits
|
||||
Josua Blejeru <josua@blejeru.com> - 8 commits
|
||||
Tobias <tobias.meier109@gmail.com> - 8 commits
|
||||
Jimmeh <jimmeh@gmail.com> - 9 commits
|
||||
Kashif Sohail <kashif.ezone@gmail.com> - 9 commits
|
||||
Liss-Bot <liss-bot@d0h.co> - 11 commits
|
||||
Tobias <96661824+CrazyWolf13@users.noreply.github.com> - 12 commits
|
||||
Alejandro Pinar Ruiz <alejandropinarruiz@gmail.com> - 14 commits
|
||||
Alicia Sykes <=> - 16 commits
|
||||
github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> - 16 commits
|
||||
@ -142,14 +154,14 @@ snyk-bot <snyk-bot@users.noreply.github.com> - 18 commits
|
||||
Matthias Bilger <matthias@bilger.info> - 19 commits
|
||||
aterox <church.kennetha@gmail.com> - 19 commits
|
||||
EVOTk <45015615+EVOTk@users.noreply.github.com> - 24 commits
|
||||
snyk-bot <snyk-bot@snyk.io> - 24 commits
|
||||
Marcell Fülöp <marekful@protonmail.com> - 27 commits
|
||||
Alicia Sykes <alicia@futr.ai> - 28 commits
|
||||
Lissy93 <gh@d0h.co> - 78 commits
|
||||
snyk-bot <snyk-bot@snyk.io> - 83 commits
|
||||
repo-visualizer <repo-visualizer@users.noreply.github.com> - 120 commits
|
||||
Lissy93 <Lissy93@users.noreply.github.com> - 217 commits
|
||||
liss-bot <liss-bot@d0h.co> - 218 commits
|
||||
Alicia Bot <87835202+liss-bot@users.noreply.github.com> - 219 commits
|
||||
Alicia Sykes <alicia@omg.lol> - 375 commits
|
||||
Alicia Sykes <gh@d0h.co> - 440 commits
|
||||
Alicia Sykes <sykes.alicia@gmail.com> - 1488 commits
|
||||
repo-visualizer <repo-visualizer@users.noreply.github.com> - 128 commits
|
||||
Lissy93 <Lissy93@users.noreply.github.com> - 222 commits
|
||||
Alicia Bot <87835202+liss-bot@users.noreply.github.com> - 240 commits
|
||||
liss-bot <liss-bot@d0h.co> - 244 commits
|
||||
Alicia Sykes <gh@d0h.co> - 439 commits
|
||||
Alicia Sykes <alicia@omg.lol> - 471 commits
|
||||
Alicia Sykes <sykes.alicia@gmail.com> - 1488 commits
|
||||
|
13
.github/workflows/apply-done-label.yml
vendored
13
.github/workflows/apply-done-label.yml
vendored
@ -1,13 +0,0 @@
|
||||
# When a PR is merged, any associated issues will have a Done label applied
|
||||
# The label will depend on the issue type, see: ./github/close-label.yml
|
||||
name: 💡 Apply Done Label
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, merged, closed]
|
||||
jobs:
|
||||
triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: logerfo/close-label@0.0.4
|
||||
with:
|
||||
repo-token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
15
.github/workflows/assign-reviewer.yml
vendored
15
.github/workflows/assign-reviewer.yml
vendored
@ -1,15 +0,0 @@
|
||||
# Automatically assigns the author as a reviewer to opened PRs and issues
|
||||
name: 💡 Auto-Assign Author to PR
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
issues:
|
||||
types: [opened]
|
||||
jobs:
|
||||
assign-author:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Assign author
|
||||
uses: technote-space/assign-author@v1
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
5
.github/workflows/auto-tag-pr.yml
vendored
5
.github/workflows/auto-tag-pr.yml
vendored
@ -8,6 +8,8 @@ on:
|
||||
jobs:
|
||||
tag-pre-release:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
outputs:
|
||||
tag: ${{ steps.autotag.outputs.tagname }}
|
||||
steps:
|
||||
@ -25,8 +27,9 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ncipollo/release-action@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ needs.tag-pre-release.outputs.tag }}
|
||||
bodyFile: ".github/LATEST_CHANGELOG.md"
|
||||
mark-issue-fixed:
|
||||
|
25
.github/workflows/build-app.yml
vendored
25
.github/workflows/build-app.yml
vendored
@ -1,25 +0,0 @@
|
||||
# This action builds and deploys the master branch
|
||||
name: 🏗️ Build App to Branch
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout 🛎️
|
||||
uses: actions/checkout@v2.3.1
|
||||
- name: Install and Build 🔧
|
||||
run: |
|
||||
npm install
|
||||
npm run build
|
||||
- name: Deploy 🚀
|
||||
uses: JamesIves/github-pages-deploy-action@4.1.4
|
||||
if: ${{ github.repository_owner == 'lissy93' }}
|
||||
with:
|
||||
branch: dev-demo
|
||||
folder: dist
|
||||
|
18
.github/workflows/build-docs-site.yml
vendored
Normal file
18
.github/workflows/build-docs-site.yml
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
# Builds and published Dashy's documentation website
|
||||
name: 📝 Build Docs Site
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ WEBSITE/docs-site-source ]
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/WEBSITE/docs-site-source'
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: redkubes/docusaurus-deploy-action@v1
|
||||
with:
|
||||
source-folder: ./
|
||||
git-user: ${{ github.actor }}
|
||||
git-password: ${{ secrets.GITHUB_TOKEN }}
|
||||
deployment-branch: gh-pages
|
13
.github/workflows/cache-artifacts.yml
vendored
13
.github/workflows/cache-artifacts.yml
vendored
@ -1,13 +0,0 @@
|
||||
# Caches artifacts, including NPM dependencies, to speed up build times
|
||||
name: 🏗️ Caching Artifacts
|
||||
on: push
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
22
.github/workflows/check-duplicate-issues.yml
vendored
22
.github/workflows/check-duplicate-issues.yml
vendored
@ -1,22 +0,0 @@
|
||||
# Attempts to auto-detect weather an issue is a duplicate, and adds a comment
|
||||
name: 🎯 Issue Duplicate Check
|
||||
on:
|
||||
issues:
|
||||
types: [opened, edited]
|
||||
jobs:
|
||||
check-duplicate:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: wow-actions/potential-duplicates@v1
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
filter: ''
|
||||
exclude: '[BUG] [QUESTION] [FEEDBACK] [SHOWCASE]'
|
||||
label: '🕸️ Potential Duplicate'
|
||||
state: all
|
||||
threshold: 0.75
|
||||
reactions: 'eyes'
|
||||
comment: >
|
||||
Potential duplicates: {{#issues}}
|
||||
- [#{{ number }}] {{ title }} ({{ accuracy }}%)
|
||||
{{/issues}}
|
20
.github/workflows/code-linter.yml
vendored
20
.github/workflows/code-linter.yml
vendored
@ -1,20 +0,0 @@
|
||||
# Lints code merged into master branch
|
||||
name: 🌈 Lint Code Base
|
||||
on:
|
||||
pull_request:
|
||||
branches: [master]
|
||||
jobs:
|
||||
build:
|
||||
name: Lint Code Base
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Lint Code Base
|
||||
uses: github/super-linter@v4
|
||||
env:
|
||||
VALIDATE_ALL_CODEBASE: false
|
||||
DEFAULT_BRANCH: master
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
30
.github/workflows/code-spell-check.yml
vendored
30
.github/workflows/code-spell-check.yml
vendored
@ -1,30 +0,0 @@
|
||||
# Finds typos in the English language within the app, submits a PR with fixes
|
||||
name: 🌈 Spelling Auto-Fix
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
types: [opened]
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
if: startsWith(github.head_ref, 'AUTO/') == false
|
||||
uses: actions/checkout@v2
|
||||
- name: Spell Check
|
||||
if: startsWith(github.head_ref, 'AUTO/') == false
|
||||
uses: sobolevn/misspell-fixer-action@master
|
||||
- name: Create PR
|
||||
uses: peter-evans/create-pull-request@v3.10.1
|
||||
if: startsWith(github.head_ref, 'AUTO/') == false
|
||||
with:
|
||||
token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
base: master
|
||||
commit-message: ':pencil2: Auto-fix typos in text'
|
||||
title: '[AUTO] Fix spelling and language'
|
||||
branch: 'AUTO/spelling-corrections'
|
||||
committer: 'Liss-Bot <alicia-gh-bot@mail.as93.net>'
|
||||
assignees: Lissy93
|
||||
reviewers: Lissy93
|
46
.github/workflows/docker-build-publish.yml
vendored
46
.github/workflows/docker-build-publish.yml
vendored
@ -11,6 +11,15 @@ on:
|
||||
- 'src/**'
|
||||
- 'public/**'
|
||||
- 'services/**'
|
||||
- '.github/workflows/docker-build-publish.yml'
|
||||
- 'Dockerfile'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
|
||||
env:
|
||||
DH_IMAGE: ${{ secrets.DOCKER_REPO }}
|
||||
@ -30,12 +39,6 @@ jobs:
|
||||
uses: tyankatsu0105/read-package-version-actions@v1
|
||||
id: package-version
|
||||
|
||||
# - name: ✨ Validate Dockerfile
|
||||
# uses: ghe-actions/dockerfile-validator@v1
|
||||
# with:
|
||||
# dockerfile: 'Dockerfile'
|
||||
# lint: 'hadolint'
|
||||
|
||||
- name: 🗂️ Make Docker Meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
@ -43,7 +46,6 @@ jobs:
|
||||
images: |
|
||||
${{ env.DH_IMAGE }}
|
||||
ghcr.io/${{ env.GH_IMAGE }}
|
||||
# ${{ secrets.ACR_SERVER }}/${{ secrets.ACR_USERNAME }}
|
||||
tags: |
|
||||
type=ref,event=tag,prefix=release-,suffix={{tag}}
|
||||
type=semver,pattern={{raw}},value=${{ steps.package-version.outputs.version }}
|
||||
@ -57,9 +59,22 @@ jobs:
|
||||
|
||||
- name: 🔧 Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
|
||||
- name: 🔧 Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
id: buildx
|
||||
with:
|
||||
driver-opts: image=moby/buildkit:v0.10.6
|
||||
|
||||
- name: 👀 Inspect builder
|
||||
run: |
|
||||
echo "Name: ${{ steps.buildx.outputs.name }}"
|
||||
echo "Endpoint: ${{ steps.buildx.outputs.endpoint }}"
|
||||
echo "Status: ${{ steps.buildx.outputs.status }}"
|
||||
echo "Flags: ${{ steps.buildx.outputs.flags }}"
|
||||
echo "Platforms: ${{ steps.buildx.outputs.platforms }}"
|
||||
|
||||
- name: 🔑 Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
@ -74,13 +89,6 @@ jobs:
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# - name: 🔑 Login to Azure Container Registry
|
||||
# uses: docker/login-action@v1
|
||||
# with:
|
||||
# registry: ${{ secrets.ACR_SERVER }}
|
||||
# username: ${{ secrets.ACR_USERNAME }}
|
||||
# password: ${{ secrets.ACR_PASSWORD }}
|
||||
|
||||
- name: 🚦 Check Registry Status
|
||||
uses: crazy-max/ghaction-docker-status@v1
|
||||
|
||||
@ -93,13 +101,3 @@ jobs:
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
push: true
|
||||
|
||||
# - name: 💬 Set Docker Hub Description
|
||||
# uses: peter-evans/dockerhub-description@v2
|
||||
# with:
|
||||
# repository: lissy93/dashy
|
||||
# readme-filepath: ./docker/docker-readme.md
|
||||
# short-description: Dashy - A self-hosted start page for your server
|
||||
# username: ${{ secrets.DOCKER_USERNAME }}
|
||||
# password: ${{ secrets.DOCKER_USER_PASS }}
|
||||
|
||||
|
63
.github/workflows/docker-publish.yml
vendored
63
.github/workflows/docker-publish.yml
vendored
@ -1,63 +0,0 @@
|
||||
# Builds a Docker image from with latest tag when master branch is updated
|
||||
# The publishes it to GitHub container registry as a package
|
||||
name: 🏗️ Build + Publish Docker Image to GHCR
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: ['master']
|
||||
tags: [v*]
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: dashy
|
||||
jobs:
|
||||
build-and-push-image:
|
||||
runs-on: ubuntu-latest
|
||||
if: "!contains(github.event.head_commit.message, '[ci-skip]')"
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
# Fetch the code
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Prepare
|
||||
id: prep
|
||||
run: echo ::set-output name=version::${GITHUB_REF##*/}
|
||||
|
||||
# Use QEMU for multi-architecture builds
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
with:
|
||||
platforms: all
|
||||
|
||||
# # Use Moby BuildKit
|
||||
# - name: Set up Docker Buildx
|
||||
# id: buildx
|
||||
# uses: docker/setup-buildx-action@v1
|
||||
# with:
|
||||
# install: true
|
||||
# version: latest
|
||||
# driver-opts: image=moby/buildkit:master
|
||||
|
||||
# Login into GH container registry
|
||||
- name: Log in to registry
|
||||
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
|
||||
|
||||
# Build the image from default Dockerfile
|
||||
- name: Build image
|
||||
run: docker build . --file docker/Dockerfile-multi-arch --tag $IMAGE_NAME --label "runnumber=${GITHUB_RUN_ID}"
|
||||
|
||||
# Push build image
|
||||
- name: Push image
|
||||
run: |
|
||||
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME
|
||||
IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]')
|
||||
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
|
||||
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
|
||||
[ "$VERSION" == "master" ] && VERSION=latest
|
||||
echo IMAGE_ID=$IMAGE_ID
|
||||
echo VERSION=$VERSION
|
||||
docker tag $IMAGE_NAME $IMAGE_ID:$VERSION
|
||||
docker push $IMAGE_ID:$VERSION
|
||||
|
33
.github/workflows/docs-link-checker.yml
vendored
33
.github/workflows/docs-link-checker.yml
vendored
@ -1,33 +0,0 @@
|
||||
# Checks for any broken links in the docs, and raises an issue if found
|
||||
name: 🌈 Broken Link Checker
|
||||
on:
|
||||
repository_dispatch:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 1 1 * *' # Run monthly
|
||||
jobs:
|
||||
link-checker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Load Excludes
|
||||
run: |
|
||||
LYCHEE_EXCLUDE=$(sed -e :a -e 'N;s/\n/ /;ta' .github/.lycheeexclude)
|
||||
echo "LYCHEE_EXCLUDE=$LYCHEE_EXCLUDE" >> $GITHUB_ENV
|
||||
|
||||
- name: Check for Broken Links
|
||||
uses: lycheeverse/lychee-action@v1.0.8
|
||||
with:
|
||||
args: --verbose -a 200,302,304,429 --exclude ${{ env.LYCHEE_EXCLUDE }} --exclude-mail --no-progress **/*.md
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.BOT_GITHUB_TOKEN}}
|
||||
LYCHEE_OUT: .github/broken-link-report.md
|
||||
|
||||
- name: Raise an Issue with Results
|
||||
uses: peter-evans/create-issue-from-file@v3
|
||||
with:
|
||||
token: ${{secrets.BOT_GITHUB_TOKEN}}
|
||||
title: '[DOCS] Broken Links found in Documentation'
|
||||
content-filepath: .github/broken-link-report.md
|
||||
labels: '📕 Docs, 👩💻 Good First Issue, 💤 Low Priority, 🤖 Auto'
|
43
.github/workflows/domain-expirey-check.yml
vendored
43
.github/workflows/domain-expirey-check.yml
vendored
@ -1,43 +0,0 @@
|
||||
name: 📕 Check Docs Domain Expiry
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 1 * * 0' # At 01:00 on Sunday.
|
||||
jobs:
|
||||
check-domain:
|
||||
runs-on: ubuntu-latest
|
||||
name: Check domain
|
||||
strategy:
|
||||
matrix:
|
||||
domain:
|
||||
- https://dashy.to
|
||||
steps:
|
||||
- name: Check domain SSL and registry expire date
|
||||
id: check-domain
|
||||
uses: codex-team/action-check-domain@v1
|
||||
with:
|
||||
url: ${{ matrix.domain }}
|
||||
- name: Raise issue if domain expiring soon
|
||||
if: ${{ steps.check-domain.outputs.paid-till-days-left && steps.check-domain.outputs.paid-till-days-left < 30 }}
|
||||
uses: rishabhgupta/git-action-issue@v2
|
||||
with:
|
||||
token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
assignees: Lissy93
|
||||
title: '[WEBSITE] Domain Expiring Soon'
|
||||
body: >
|
||||
**Priority Notice**
|
||||
Domain, ${{ matrix.domain }} will expire in ${{ steps.check-domain.outputs.paid-till-days-left }} days.
|
||||
@Lissy93 - Please take action immediately to prevent any downtime
|
||||
|
||||
- name: Raise issue if SSL Cert expiring soon
|
||||
if: ${{ steps.check-domain.outputs.ssl-expire-days-left && steps.check-domain.outputs.ssl-expire-days-left < 14 }}
|
||||
uses: rishabhgupta/git-action-issue@v2
|
||||
with:
|
||||
token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
assignees: Lissy93
|
||||
title: '[WEBSITE] SSL Cert Expiring Soon'
|
||||
body: >
|
||||
**Priority Notice**
|
||||
The SSL Certificate for ${{ matrix.domain }} will expire in ${{ steps.check-domain.outputs.ssl-expire-days-left }} days, on ${{ steps.check-domain.outputs.ssl-expire-date }}.
|
||||
@Lissy93 - Please take action immediately to prevent any downtime
|
||||
|
18
.github/workflows/issue-translator.yml
vendored
18
.github/workflows/issue-translator.yml
vendored
@ -1,18 +0,0 @@
|
||||
# Will translate any issues opened in foraign language, and add the English translation as a comment
|
||||
name: 🎯 Issue Translator
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: tomsun28/issues-translate-action@v2.5
|
||||
with:
|
||||
BOT_GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
BOT_LOGIN_NAME: liss-bot
|
||||
IS_MODIFY_TITLE: true
|
||||
CUSTOM_BOT_NOTE: It looks like this issue isn't in English - not a problem, here's the translation! 🇬🇧
|
16
.github/workflows/label-sponsors.yml
vendored
16
.github/workflows/label-sponsors.yml
vendored
@ -1,16 +0,0 @@
|
||||
# Adds a label to any issues raised by a sponsor of Lissy93/Dashy
|
||||
# In order to allow their request can be prioritized
|
||||
name: 🎯 Label sponsors
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
jobs:
|
||||
build:
|
||||
name: is-sponsor-label
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: JasonEtco/is-sponsor-label-action@v1
|
||||
with:
|
||||
label: Priority Request - Sponsor 💖
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
18
.github/workflows/label-top-issues.yml
vendored
18
.github/workflows/label-top-issues.yml
vendored
@ -1,18 +0,0 @@
|
||||
# Applies the 'Top Issue' label to tickets with most user reactions
|
||||
name: 🎯 Label Top Issues
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 1 * * *' # Run at 01:00 each day
|
||||
jobs:
|
||||
labelTopIssues:
|
||||
name: Label Top Issues
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Label Issues
|
||||
uses: adamzolyak/top-issues-action@master
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
TOP_NUMBER_OF_ISSUES: 10
|
||||
TOP_LABEL_NAME: "👍 Top 10 Issue!"
|
||||
TOP_LABEL_COLOR: FBCA04
|
20
.github/workflows/mind-your-language.yml
vendored
20
.github/workflows/mind-your-language.yml
vendored
@ -1,20 +0,0 @@
|
||||
# Detects offensive language in comments and takes reaction
|
||||
name: 🎯 Mind your language
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request:
|
||||
types: [opened]
|
||||
jobs:
|
||||
echo_issue_comment:
|
||||
runs-on: ubuntu-latest
|
||||
name: profanity check
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Profanity check step
|
||||
uses: tailaiw/mind-your-language-action@v1.0.3
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
13
.github/workflows/pr-commenter.yml
vendored
13
.github/workflows/pr-commenter.yml
vendored
@ -1,13 +0,0 @@
|
||||
# Adds comments to PR, based on which files are modified
|
||||
name: 💡 PR Commenter
|
||||
on: [ pull_request_target ]
|
||||
jobs:
|
||||
pr-comment:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: exercism/pr-commenter-action@v1.3.0
|
||||
with:
|
||||
github-token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
config-file: .github/pr-auto-comments.yml
|
||||
template-variables: |
|
||||
{ "prAuthor": "${{ github.event.pull_request.user.login }}" }
|
12
.github/workflows/pr-labler.yml
vendored
12
.github/workflows/pr-labler.yml
vendored
@ -1,12 +0,0 @@
|
||||
# Labels pull requests based on their branch name
|
||||
name: 💡 PR Branch Labeler
|
||||
on: pull_request
|
||||
jobs:
|
||||
label-pr:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Label PR
|
||||
if: github.event.action == 'opened'
|
||||
uses: ffittschen/pr-branch-labeler@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
13
.github/workflows/raise-issue-from-todo.yml
vendored
13
.github/workflows/raise-issue-from-todo.yml
vendored
@ -1,13 +0,0 @@
|
||||
# Finds any TO-DO notes within the code, and opens up an issue for it to be fixed
|
||||
name: 🌈 Open issue for Todos
|
||||
on: ["push"]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- uses: "actions/checkout@master"
|
||||
- name: "TODO to Issue"
|
||||
uses: "alstr/todo-to-issue-action@v4.2"
|
||||
id: "todo"
|
||||
with:
|
||||
TOKEN: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
92
.github/workflows/repo-visualization.yml
vendored
92
.github/workflows/repo-visualization.yml
vendored
@ -1,92 +0,0 @@
|
||||
# Generates series of diagrams and visualizations
|
||||
name: 📊 Generate Repo Stats
|
||||
on:
|
||||
workflow_dispatch: # Manual dispatch
|
||||
schedule:
|
||||
- cron: '0 1 * * 0' # At 01:00 on Sunday.
|
||||
|
||||
jobs:
|
||||
# File structure chart
|
||||
file-structure:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@master
|
||||
- name: Generate File Structure Diagram
|
||||
uses: githubocto/repo-visualizer@0.7.1
|
||||
with:
|
||||
root_path: 'src/'
|
||||
output_file: docs/assets/repo-visualization.svg
|
||||
excluded_paths: dist,node_modules
|
||||
commit_message: ':yellow_heart: Updates repo diagram'
|
||||
branch: master
|
||||
|
||||
# Hercules git branching stats
|
||||
git-stats:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Hercules
|
||||
uses: src-d/hercules@master
|
||||
- uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: hercules_charts
|
||||
path: hercules_charts.tar
|
||||
|
||||
# Lowlighter metrics community metrics
|
||||
community-stats:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@master
|
||||
- name: Generate Repo Metrics
|
||||
uses: lowlighter/metrics@latest
|
||||
with:
|
||||
token: ${{ secrets.LISSY93_PAT }}
|
||||
committer_token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
committer_branch: master
|
||||
committer_message: ':purple_heart: Adds repo metrics'
|
||||
filename: docs/assets/repo-metrics.*
|
||||
template: classic
|
||||
user: Lissy93
|
||||
repo: dashy
|
||||
delay: 5
|
||||
|
||||
- name: Generate License Metrics
|
||||
uses: lowlighter/metrics@latest
|
||||
with:
|
||||
token: ${{ secrets.LISSY93_PAT }}
|
||||
committer_token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
committer_branch: master
|
||||
committer_message: ':purple_heart: Adds license metrics'
|
||||
filename: docs/assets/license-metrics.*
|
||||
template: repository
|
||||
user: Lissy93
|
||||
repo: dashy
|
||||
delay: 5
|
||||
plugin_licenses: yes
|
||||
plugin_licenses_setup: yarn build
|
||||
plugin_licenses_ratio: yes
|
||||
plugin_licenses_legal: yes
|
||||
|
||||
- name: Generate Contributor Metrics
|
||||
uses: lowlighter/metrics@latest
|
||||
with:
|
||||
token: ${{ secrets.LISSY93_PAT }}
|
||||
committer_token: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
committer_branch: master
|
||||
committer_message: ':purple_heart: Adds contributor metrics'
|
||||
filename: docs/assets/controbutor-metrics.*
|
||||
user: Lissy93
|
||||
repo: dashy
|
||||
delay: 5
|
||||
plugin_contributors: yes
|
||||
plugin_contributors_base: ""
|
||||
plugin_contributors_head: master
|
||||
plugin_contributors_ignored: bot
|
||||
plugin_contributors_contributions: yes
|
||||
plugin_contributors_sections: contributors
|
||||
|
15
.github/workflows/save-repo-analytics.yml
vendored
15
.github/workflows/save-repo-analytics.yml
vendored
@ -1,15 +0,0 @@
|
||||
name: 📊 Save Repo Analytics
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 1 * * 0' # At 01:00 on Sunday.
|
||||
jobs:
|
||||
gen-stats:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Repo Analytics
|
||||
uses: jgehrcke/github-repo-stats@HEAD
|
||||
with:
|
||||
repository: lissy93/dashy
|
||||
databranch: DATA/repo-stats
|
||||
ghtoken: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
19
.github/workflows/security-scanning.yml
vendored
19
.github/workflows/security-scanning.yml
vendored
@ -1,19 +0,0 @@
|
||||
# Uses Snyk to check for potential vulnerabilities, then sends results to GH security tab
|
||||
name: 💡 Vulnerability Scanning
|
||||
on: push
|
||||
jobs:
|
||||
security:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- name: Run Snyk to check for vulnerabilities
|
||||
uses: snyk/actions/node@master
|
||||
continue-on-error: true
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
with:
|
||||
args: --sarif-file-output=snyk.sarif
|
||||
- name: Upload result to GitHub Code Scanning
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
with:
|
||||
sarif_file: snyk.sarif
|
17
.github/workflows/unfurl-links.yml
vendored
17
.github/workflows/unfurl-links.yml
vendored
@ -1,17 +0,0 @@
|
||||
# Expands any raw pasted link in comments. Useful so people know what they're clicking
|
||||
name: 🎯 Unfurl Links
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request:
|
||||
types: [opened]
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: wow-actions/unfurl-links@v1
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
raw: true
|
58
.github/workflows/update-docs-site.yml
vendored
Normal file
58
.github/workflows/update-docs-site.yml
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
name: 📝 Update Documentation
|
||||
|
||||
# This will run whenever the /docs directory in master branch is updated,
|
||||
# or if the workflow is manually dispatched, plus a sync check on Sun at 03:30 UTC
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '30 3 * * 0'
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- 'docs/**'
|
||||
|
||||
|
||||
# Jobs to be run:
|
||||
# 1. Checkout master branch
|
||||
# 2. Checkout website source code branch
|
||||
# 3. Install Python
|
||||
# 4. Copy /docs from master to website branch
|
||||
# 5. Run the script which processes documentation
|
||||
# 6. Commit and push updated docs to the website source code branch
|
||||
jobs:
|
||||
update-docs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout master branch 🛎️
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: 'master-docs'
|
||||
|
||||
- name: Checkout WEBSITE/docs-site-source branch 🛎️
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: 'WEBSITE/docs-site-source'
|
||||
path: 'website-docs'
|
||||
|
||||
- name: Install Python 🐍
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Run script to update documentation 🪄
|
||||
working-directory: website-docs
|
||||
run: |
|
||||
cp -r ../master-docs/docs ./
|
||||
python ./do-markdown-magic.py
|
||||
|
||||
- name: Commit changes 🚀
|
||||
run: |
|
||||
cd website-docs
|
||||
git config --local user.email "liss-bot@d0h.co"
|
||||
git config --local user.name "Liss-Bot"
|
||||
git add docs
|
||||
git commit -m "Update documentation" || echo "No changes to commit"
|
||||
git push
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
1
.yarnrc.yml
Normal file
1
.yarnrc.yml
Normal file
@ -0,0 +1 @@
|
||||
nodeLinker: node-modules
|
12
Dockerfile
12
Dockerfile
@ -1,4 +1,4 @@
|
||||
FROM node:16.13.2-alpine AS BUILD_IMAGE
|
||||
FROM node:18.19.1-alpine AS BUILD_IMAGE
|
||||
|
||||
# Set the platform to build image for
|
||||
ARG TARGETPLATFORM
|
||||
@ -16,7 +16,7 @@ WORKDIR /app
|
||||
|
||||
# Install app dependencies
|
||||
COPY package.json yarn.lock ./
|
||||
RUN yarn install --frozen-lockfile --network-timeout 1000000
|
||||
RUN yarn install --ignore-engines --immutable --no-cache --network-timeout 300000 --network-concurrency 1
|
||||
|
||||
# Copy over all project files and folders to the working directory
|
||||
COPY . ./
|
||||
@ -25,10 +25,10 @@ COPY . ./
|
||||
RUN yarn build --mode production
|
||||
|
||||
# Production stage
|
||||
FROM node:16.13.2-alpine
|
||||
FROM node:20.11.1-alpine3.19
|
||||
|
||||
# Define some ENV Vars
|
||||
ENV PORT=80 \
|
||||
ENV PORT=8080 \
|
||||
DIRECTORY=/app \
|
||||
IS_DOCKER=true
|
||||
|
||||
@ -40,11 +40,9 @@ RUN apk add --no-cache tzdata
|
||||
|
||||
# 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
|
||||
CMD [ "yarn", "start" ]
|
||||
CMD [ "yarn", "build-and-start" ]
|
||||
|
||||
# Expose the port
|
||||
EXPOSE ${PORT}
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-2022 Alicia Sykes <https://aliciasykes.com>
|
||||
Copyright (c) 2021-2024 Alicia Sykes <https://aliciasykes.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
89
README.md
89
README.md
@ -124,7 +124,7 @@ Or
|
||||
```docker
|
||||
docker run -d \
|
||||
-p 4000:80 \
|
||||
-v /root/my-local-conf.yml:/app/public/conf.yml \
|
||||
-v /root/my-local-conf.yml:/app/user-data/conf.yml \
|
||||
--name my-dashboard \
|
||||
--restart=always \
|
||||
lissy93/dashy:latest
|
||||
@ -140,7 +140,7 @@ See also: [examples with Docker Compose](./docs/deployment.md#using-docker-compo
|
||||
You will need [git](https://git-scm.com/downloads), the latest or LTS version of [Node.js](https://nodejs.org/) and _(optionally)_ [Yarn](https://yarnpkg.com/) installed on your system.
|
||||
|
||||
- Clone the Repo: `git clone https://github.com/Lissy93/dashy.git` and `cd dashy`
|
||||
- Configuration: Fill in your settings in `./public/conf.yml`
|
||||
- Configuration: Fill in your settings in `./user-data/conf.yml`
|
||||
- Install dependencies: `yarn`
|
||||
- Build: `yarn build`
|
||||
- Run: `yarn start`
|
||||
@ -154,6 +154,7 @@ Dashy supports **1-Click deployments** on several popular cloud platforms. To sp
|
||||
- [<img src="https://i.ibb.co/d2P1WZ7/heroku.png" width="18"/> Deploy to Heroku](https://heroku.com/deploy?template=https://github.com/Lissy93/dashy)
|
||||
- [<img src="https://i.ibb.co/Ld2FZzb/vercel.png" width="18"/> Deploy to Vercel](https://vercel.com/new/project?template=https://github.com/lissy93/dashy)
|
||||
- [<img src="https://i.ibb.co/xCHtzgh/render.png" width="18"/> Deploy to Render](https://render.com/deploy?repo=https://github.com/lissy93/dashy/tree/deploy_render)
|
||||
- [<img src="https://railway.app/brand/logo-light.png" width="18"/> Deploy to Railway](https://railway.app/template/MtdjAQ?referralCode=app)
|
||||
- [<img src="https://i.ibb.co/J7MGymY/googlecloud.png" width="18"/> Deploy to GCP](https://deploy.cloud.run/?git_repo=https://github.com/lissy93/dashy.git)
|
||||
- [<img src="https://i.ibb.co/HVWVYF7/docker.png" width="18"/> Deploy to PWD](https://labs.play-with-docker.com/?stack=https://raw.githubusercontent.com/Lissy93/dashy/master/docker-compose.yml)
|
||||
- [<img src="https://i.ibb.co/7NxnM2P/easypanel.png" width="18"/> Deploy to Easypanel](https://easypanel.io/docs/templates/dashy)
|
||||
@ -168,7 +169,7 @@ Dashy supports **1-Click deployments** on several popular cloud platforms. To sp
|
||||
|
||||
> For full configuration documentation, see: [**Configuring**](./docs/configuring.md)
|
||||
|
||||
Dashy is configured through a YAML file, located at `./public/conf.yml`. In addition, you can find a complete list of available options in the [Configuring Docs](./docs/configuring.md). The config can also be edited and saved directly through the UI.
|
||||
Dashy is configured through a YAML file, located at `./user-data/conf.yml`. In addition, you can find a complete list of available options in the [Configuring Docs](./docs/configuring.md). The config can also be edited and saved directly through the UI.
|
||||
|
||||
**[⬆️ Back to Top](#dashy)**
|
||||
|
||||
@ -538,6 +539,13 @@ Huge thanks to the sponsors helping to support Dashy's development!
|
||||
<sub><b>Vincent Koc</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Admonstrator">
|
||||
<img src="https://avatars.githubusercontent.com/u/69824?u=1e226d7a36cdd661c3e4cd486fea140d045b7d57&v=4" width="80;" alt="Admonstrator"/>
|
||||
<br />
|
||||
<sub><b>Aaron Viehl</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/peng1can">
|
||||
<img src="https://avatars.githubusercontent.com/u/225854?v=4" width="80;" alt="peng1can"/>
|
||||
@ -547,7 +555,7 @@ Huge thanks to the sponsors helping to support Dashy's development!
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/tbjers">
|
||||
<img src="https://avatars.githubusercontent.com/u/1117052?u=539d96d5e581b3139c75713ce35b89a36626404c&v=4" width="80;" alt="tbjers"/>
|
||||
<img src="https://avatars.githubusercontent.com/u/1117052?v=4" width="80;" alt="tbjers"/>
|
||||
<br />
|
||||
<sub><b>Torgny Bjers</b></sub>
|
||||
</a>
|
||||
@ -565,7 +573,8 @@ Huge thanks to the sponsors helping to support Dashy's development!
|
||||
<br />
|
||||
<sub><b>Anand Chowdhary</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/shrippen">
|
||||
<img src="https://avatars.githubusercontent.com/u/2873570?v=4" width="80;" alt="shrippen"/>
|
||||
@ -574,13 +583,6 @@ Huge thanks to the sponsors helping to support Dashy's development!
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/davidpaulyoung">
|
||||
<img src="https://avatars.githubusercontent.com/u/3418369?v=4" width="80;" alt="davidpaulyoung"/>
|
||||
<br />
|
||||
<sub><b>David Young</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/bile0026">
|
||||
<img src="https://avatars.githubusercontent.com/u/5022496?u=aec96ad173c0ea9baaba93807efa8a848af6595c&v=4" width="80;" alt="bile0026"/>
|
||||
@ -602,6 +604,21 @@ Huge thanks to the sponsors helping to support Dashy's development!
|
||||
<sub><b>Digital Archeology</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/InDieTasten">
|
||||
<img src="https://avatars.githubusercontent.com/u/7047377?u=8d8f8017628b38bc46dcbf3620e194b01d3fb2d1&v=4" width="80;" alt="InDieTasten"/>
|
||||
<br />
|
||||
<sub><b>InDieTasten</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/araguaci">
|
||||
<img src="https://avatars.githubusercontent.com/u/7318668?v=4" width="80;" alt="araguaci"/>
|
||||
<br />
|
||||
<sub><b>araguaci</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/bmcgonag">
|
||||
<img src="https://avatars.githubusercontent.com/u/7346620?u=2a0f9284f3e12ac1cc15288c254d1ec68a5081e8&v=4" width="80;" alt="bmcgonag"/>
|
||||
@ -615,8 +632,7 @@ Huge thanks to the sponsors helping to support Dashy's development!
|
||||
<br />
|
||||
<sub><b>Vlad Timofeev</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/helixzz">
|
||||
<img src="https://avatars.githubusercontent.com/u/12218889?u=d06d0c103dfbdb99450623064f7da3c5a3675fb6&v=4" width="80;" alt="helixzz"/>
|
||||
@ -624,13 +640,28 @@ Huge thanks to the sponsors helping to support Dashy's development!
|
||||
<sub><b>HeliXZz</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/patvdv">
|
||||
<img src="https://avatars.githubusercontent.com/u/12430107?v=4" width="80;" alt="patvdv"/>
|
||||
<br />
|
||||
<sub><b>Patrick Van Der Veken</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/mryesiller">
|
||||
<img src="https://avatars.githubusercontent.com/u/24632172?u=0d20f2d615158f87cd60a3398d3efb026c32f291&v=4" width="80;" alt="mryesiller"/>
|
||||
<br />
|
||||
<sub><b>Göksel Yeşiller</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/undefined">
|
||||
<img src="" width="80;" alt="undefined"/>
|
||||
<br />
|
||||
<sub><b>Undefined</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Bastii717">
|
||||
<img src="https://avatars.githubusercontent.com/u/53431819?u=604977bed6ad6875ada890d0d3765a4cacc2fa14&v=4" width="80;" alt="Bastii717"/>
|
||||
@ -639,17 +670,31 @@ Huge thanks to the sponsors helping to support Dashy's development!
|
||||
</a>
|
||||
</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"/>
|
||||
<a href="https://github.com/M2TD">
|
||||
<img src="https://avatars.githubusercontent.com/u/85460457?v=4" width="80;" alt="M2TD"/>
|
||||
<br />
|
||||
<sub><b>Ratty222</b></sub>
|
||||
<sub><b>M2TD</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/CrazyWolf13">
|
||||
<img src="https://avatars.githubusercontent.com/u/96661824?v=4" width="80;" alt="CrazyWolf13"/>
|
||||
<a href="https://github.com/frankdez93">
|
||||
<img src="https://avatars.githubusercontent.com/u/87549420?v=4" width="80;" alt="frankdez93"/>
|
||||
<br />
|
||||
<sub><b>Tobias</b></sub>
|
||||
<sub><b>Frankdez93</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/NixyJuppie">
|
||||
<img src="https://avatars.githubusercontent.com/u/138570196?u=b102c222487905728b858704962d32759df29ebe&v=4" width="80;" alt="NixyJuppie"/>
|
||||
<br />
|
||||
<sub><b>Nixy</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/nrvo">
|
||||
<img src="https://avatars.githubusercontent.com/u/151435968?u=e1dcb307fd0efdc45cddbe9490a7b956e4da6835&v=4" width="80;" alt="nrvo"/>
|
||||
<br />
|
||||
<sub><b>Nrvo</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
</table>
|
||||
@ -764,7 +809,7 @@ A few self-hosted web apps serve a similar purpose to Dashy. If you're looking f
|
||||
Dashy is Licensed under [MIT X11](https://en.wikipedia.org/wiki/MIT_License)
|
||||
|
||||
```
|
||||
Copyright © 2021-2022 Alicia Sykes <https://aliciasykes.com>
|
||||
Copyright © 2021-2024 Alicia Sykes <https://aliciasykes.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
software and associated documentation files (the "Software"), to deal in the Software
|
||||
|
@ -12,21 +12,17 @@ services:
|
||||
# To build from source, replace 'image: lissy93/dashy' with 'build: .'
|
||||
# build: .
|
||||
|
||||
# Or, to use a Dockerfile for your archtecture, uncomment the following
|
||||
# context: .
|
||||
# dockerfile: ./docker/Dockerfile-arm32v7
|
||||
|
||||
# You can also use an image with a different tag, or pull from a different registry, e.g:
|
||||
# image: ghcr.io/lissy93/dashy or image: lissy93/dashy:arm64v8
|
||||
# image: ghcr.io/lissy93/dashy or image: lissy93/dashy:3.0.0
|
||||
|
||||
# Pass in your config file below, by specifying the path on your host machine
|
||||
# volumes:
|
||||
# - /path/to/my-config.yml:/app/public/conf.yml
|
||||
# - /path/to/item-icons:/app/public/item-icons
|
||||
# - /path/to/my-config.yml:/app/user-data/conf.yml
|
||||
# - /path/to/item-icons:/app/user-data/item-icons/
|
||||
|
||||
# Set port that web service will be served on. Keep container port as 80
|
||||
ports:
|
||||
- 4000:80
|
||||
- 4000:8080
|
||||
|
||||
# Set any environmental variables
|
||||
environment:
|
||||
|
@ -112,7 +112,7 @@ Dashy can also be run on bare metal using Node.js, or deployed to a cloud servic
|
||||
Dashy is Licensed under [MIT X11](https://en.wikipedia.org/wiki/MIT_License)
|
||||
|
||||
```
|
||||
Copyright © 2021 Alicia Sykes <https://aliciasykes.com>
|
||||
Copyright © 2024 Alicia Sykes <https://aliciasykes.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
software and associated documentation files (the "Software"), to deal in the Software
|
||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 28 MiB After Width: | Height: | Size: 29 MiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 94 KiB |
@ -18,6 +18,12 @@
|
||||
- [OAuth Services](#oauth-services)
|
||||
- [Auth on Cloud Hosting Services](#static-site-hosting-providers)
|
||||
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Dashy's built-in auth is not indented to protect a publicly hosted instance against unauthorized access. Instead you should use an auth provider compatible with your reverse proxy, or access Dashy via your VPN.
|
||||
>
|
||||
> In cases where Dashy is only accessibly within your home network, and you just want to add a login page, then the built-in auth may be sufficient, but keep in mind that configuration can still be accessed.
|
||||
|
||||
## Built-In Auth
|
||||
|
||||
Dashy has a basic login page included, and frontend authentication. You can enable this by adding users to the `auth` section under `appConfig` in your `conf.yml`. If this section is not specified, then no authentication will be required to access the app, and the homepage will resolve to your dashboard.
|
||||
@ -257,7 +263,7 @@ In NGINX you can specify [control access](https://docs.nginx.com/nginx/admin-gui
|
||||
|
||||
```text
|
||||
server {
|
||||
listen 80;
|
||||
listen 8080;
|
||||
server_name www.dashy.example.com;
|
||||
location / {
|
||||
root /path/to/dashy/;
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Configuring
|
||||
|
||||
All app configuration is specified in [`/public/conf.yml`](https://github.com/Lissy93/dashy/blob/master/public/conf.yml) which is in [YAML Format](https://yaml.org/) format. If you're using Docker, this file can be passed in as a volume. Changes can either be made directly to this file, or done [through the UI](#editing-config-through-the-ui). From the UI you can also export, backup, reset, validate and download your configuration file.
|
||||
All app configuration is specified in [`/user-data/conf.yml`](https://github.com/Lissy93/dashy/blob/master/user-data/conf.yml) which is in [YAML Format](https://yaml.org/) format. If you're using Docker, this file can be passed in as a volume. Changes can either be made directly to this file, or done [through the UI](#editing-config-through-the-ui). From the UI you can also export, backup, reset, validate and download your configuration file.
|
||||
|
||||
## There are three ways to edit the config
|
||||
|
||||
@ -36,6 +36,7 @@ The following file provides a reference of all supported configuration options.
|
||||
- [`auth`](#appconfigauth-optional) - Built-in authentication setup
|
||||
- [`users`](#appconfigauthusers-optional) - List or users (for simple auth)
|
||||
- [`keycloak`](#appconfigauthkeycloak-optional) - Auth config for Keycloak
|
||||
- [`headerAuth`](#appconfigauthheaderauth-optional) - Auth config for HeaderAuth
|
||||
- [**`sections`**](#section) - List of sections
|
||||
- [`displayData`](#sectiondisplaydata-optional) - Section display settings
|
||||
- [`show/hideForKeycloakUsers`](#sectiondisplaydatahideforkeycloakusers-sectiondisplaydatashowforkeycloakusers-itemdisplaydatahideforkeycloakusers-and-itemdisplaydatashowforkeycloakusers) - Set user controls
|
||||
@ -104,9 +105,9 @@ The following file provides a reference of all supported configuration options.
|
||||
**`startingView`** | `enum` | _Optional_ | Which page to load by default, and on the base page or domain root. You can still switch to different views from within the UI. Can be either `default`, `minimal` or `workspace`. Defaults to `default`
|
||||
**`defaultOpeningMethod`** | `enum` | _Optional_ | The default opening method for items, if no `target` is specified for a given item. Can be either `newtab`, `sametab`, `modal`, `workspace`, `clipboard`, `top` or `parent`. Defaults to `newtab`
|
||||
**`statusCheck`** | `boolean` | _Optional_ | When set to `true`, Dashy will ping each of your services and display their status as a dot next to each item. This can be overridden by setting `statusCheck` under each item. Defaults to `false`
|
||||
**`statusCheckInterval`** | `boolean` | _Optional_ | The number of seconds between checks. If set to `0` then service will only be checked on initial page load, which is usually the desired functionality. If value is less than `10` you may experience a hit in performance. Defaults to `0`
|
||||
**`statusCheckInterval`** | `number` | _Optional_ | The number of seconds between checks. If set to `0` then service will only be checked on initial page load, which is usually the desired functionality. If value is less than `10` you may experience a hit in performance. Defaults to `0`
|
||||
**`webSearch`** | `object` | _Optional_ | Configuration options for the web search feature, set your default search engine, opening method or disable web search. See [`webSearch`](#appconfigwebsearch-optional)
|
||||
**`backgroundImg`** | `string` | _Optional_ | Path to an optional full-screen app background image. This can be either remote (http) or local (/). Note that this will slow down initial load
|
||||
**`backgroundImg`** | `string` | _Optional_ | Path to an optional full-screen app background image. This can be either remote (http) or local (relative to /app/public/item-icons/ inside the container). Note that this will slow down initial load
|
||||
**`enableFontAwesome`** | `boolean` | _Optional_ | If set to `true` font-awesome will be loaded, if set to `false` they will not be. if left blank font-awesome will be enabled only if required by 1 or more icons
|
||||
**`enableMaterialDesignIcons`** | `boolean` | _Optional_ | If set to `true` mdi icons will be loaded, if set to `false` they will not be. Where `true` is enabled, if left blank material design icons will be enabled only if required by 1 or more icons
|
||||
**`fontAwesomeKey`** | `string` | _Optional_ | If you have a font-awesome key, then you can use it here and make use of premium icons. It is a 10-digit alpha-numeric string from you're FA kit URL (e.g. `13014ae648`)
|
||||
@ -147,6 +148,8 @@ The following file provides a reference of all supported configuration options.
|
||||
**`users`** | `array` | _Optional_ | An array of objects containing usernames and hashed passwords. If this is not provided, then authentication will be off by default, and you will not need any credentials to access the app. See [`appConfig.auth.users`](#appconfigauthusers-optional). <br>**Note** this method of authentication is handled on the client side, so for security critical situations, it is recommended to use an [alternate authentication method](/docs/authentication.md#alternative-authentication-methods).
|
||||
**`enableKeycloak`** | `boolean` | _Optional_ | If set to `true`, then authentication using Keycloak will be enabled. Note that you need to have an instance running, and have also configured `auth.keycloak`. Defaults to `false`
|
||||
**`keycloak`** | `object` | _Optional_ | Config options to point Dashy to your Keycloak server. Requires `enableKeycloak: true`. See [`auth.keycloak`](#appconfigauthkeycloak-optional) for more info
|
||||
**`enableHeaderAuth`** | `boolean` | _Optional_ | If set to `true`, then authentication using HeaderAuth will be enabled. Note that you need to have your web server/reverse proxy running, and have also configured `auth.headerAuth`. Defaults to `false`
|
||||
**`headerAuth`** | `object` | _Optional_ | Config options to point Dashy to your headers for authentication. Requires `enableHeaderAuth: true`. See [`auth.headerAuth`](#appconfigauthheaderauth-optional) for more info
|
||||
**`enableGuestAccess`** | `boolean` | _Optional_ | When set to `true`, an unauthenticated user will be able to access the dashboard, with read-only access, without having to login. Requires `auth.users` to be configured. Defaults to `false`.
|
||||
|
||||
For more info, see the **[Authentication Docs](/docs/authentication.md)**
|
||||
@ -174,6 +177,15 @@ For more info, see the **[Authentication Docs](/docs/authentication.md)**
|
||||
|
||||
**[⬆️ Back to Top](#configuring)**
|
||||
|
||||
## `appConfig.auth.headerAuth` _(optional)_
|
||||
|
||||
**Field** | **Type** | **Required**| **Description**
|
||||
--- | --- | --- | ---
|
||||
**`userHeader`** | `string` | _Optional_ | The Header name which contains username (default: REMOTE_USER). Case insensitive
|
||||
**`proxyWhitelist`** | `array` | Required | An array of Upstream proxy servers to expect authencticated requests from
|
||||
|
||||
**[⬆️ Back to Top](#configuring)**
|
||||
|
||||
## `appConfig.webSearch` _(optional)_
|
||||
|
||||
**Field** | **Type** | **Required**| **Description**
|
||||
@ -228,6 +240,7 @@ For more info, see the **[Authentication Docs](/docs/authentication.md)**
|
||||
**`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
|
||||
**`rel`** | `string` | _Optional_ | The value of the `rel` attribute for the link. Useful for specifying the relationship between the target link/document and Dashy. Defaults to `noopener noreferrer`
|
||||
**`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
|
||||
**`displayData`** | `object` | _Optional_ | Meta-data to optionally override display settings for a given item. See [`displayData`](#itemdisplaydata-optional)
|
||||
@ -246,7 +259,7 @@ For more info, see the **[Authentication Docs](/docs/authentication.md)**
|
||||
|
||||
**[⬆️ Back to Top](#configuring)**
|
||||
|
||||
## `section.widget` _(optional)_
|
||||
## `section.widgets` _(optional)_
|
||||
|
||||
**Field** | **Type** | **Required**| **Description**
|
||||
--- | --- | --- | ---
|
||||
|
@ -130,7 +130,7 @@ If you like, you could also consider [subscribing to my mailing list](https://no
|
||||
|
||||
For a full list of Dashy's contributors, see the [Credits Page](/docs/credits.md)
|
||||
|
||||
[![Auto-generated contributors](https://raw.githubusercontent.com/Lissy93/dashy/master/docs/assets/CONTRIBUTORS.svg)](/docs/credits.md)
|
||||
[![Auto-generated contributors](https://raw.githubusercontent.com/Lissy93/dashy/master/docs/assets/CONTRIBUTORS.svg)](https://github.com/Lissy93/dashy/blob/master/docs/credits.md)
|
||||
|
||||
### Star-Gazers Over Time
|
||||
|
||||
|
452
docs/credits.md
452
docs/credits.md
@ -11,6 +11,13 @@
|
||||
<sub><b>Vincent Koc</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Admonstrator">
|
||||
<img src="https://avatars.githubusercontent.com/u/69824?u=1e226d7a36cdd661c3e4cd486fea140d045b7d57&v=4" width="80;" alt="Admonstrator"/>
|
||||
<br />
|
||||
<sub><b>Aaron Viehl</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/peng1can">
|
||||
<img src="https://avatars.githubusercontent.com/u/225854?v=4" width="80;" alt="peng1can"/>
|
||||
@ -20,7 +27,7 @@
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/tbjers">
|
||||
<img src="https://avatars.githubusercontent.com/u/1117052?u=539d96d5e581b3139c75713ce35b89a36626404c&v=4" width="80;" alt="tbjers"/>
|
||||
<img src="https://avatars.githubusercontent.com/u/1117052?v=4" width="80;" alt="tbjers"/>
|
||||
<br />
|
||||
<sub><b>Torgny Bjers</b></sub>
|
||||
</a>
|
||||
@ -38,21 +45,14 @@
|
||||
<br />
|
||||
<sub><b>Anand Chowdhary</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/shrippen">
|
||||
<img src="https://avatars.githubusercontent.com/u/2873570?v=4" width="80;" alt="shrippen"/>
|
||||
<br />
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/davidpaulyoung">
|
||||
<img src="https://avatars.githubusercontent.com/u/3418369?v=4" width="80;" alt="davidpaulyoung"/>
|
||||
<br />
|
||||
<sub><b>David Young</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/bile0026">
|
||||
@ -75,6 +75,21 @@
|
||||
<sub><b>Digital Archeology</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/InDieTasten">
|
||||
<img src="https://avatars.githubusercontent.com/u/7047377?u=8d8f8017628b38bc46dcbf3620e194b01d3fb2d1&v=4" width="80;" alt="InDieTasten"/>
|
||||
<br />
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/araguaci">
|
||||
<img src="https://avatars.githubusercontent.com/u/7318668?v=4" width="80;" alt="araguaci"/>
|
||||
<br />
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/bmcgonag">
|
||||
<img src="https://avatars.githubusercontent.com/u/7346620?u=2a0f9284f3e12ac1cc15288c254d1ec68a5081e8&v=4" width="80;" alt="bmcgonag"/>
|
||||
@ -88,8 +103,7 @@
|
||||
<br />
|
||||
<sub><b>Vlad Timofeev</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/helixzz">
|
||||
<img src="https://avatars.githubusercontent.com/u/12218889?u=d06d0c103dfbdb99450623064f7da3c5a3675fb6&v=4" width="80;" alt="helixzz"/>
|
||||
@ -97,13 +111,28 @@
|
||||
<sub><b>HeliXZz</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/patvdv">
|
||||
<img src="https://avatars.githubusercontent.com/u/12430107?v=4" width="80;" alt="patvdv"/>
|
||||
<br />
|
||||
<sub><b>Patrick Van Der Veken</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/mryesiller">
|
||||
<img src="https://avatars.githubusercontent.com/u/24632172?u=0d20f2d615158f87cd60a3398d3efb026c32f291&v=4" width="80;" alt="mryesiller"/>
|
||||
<br />
|
||||
<sub><b>Göksel Yeşiller</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/forwardemail">
|
||||
<img src="https://avatars.githubusercontent.com/u/32481436?v=4" width="80;" alt="forwardemail"/>
|
||||
<br />
|
||||
<sub><b>Forward Email - Open-source & Privacy-focused Email Service (2023)</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Bastii717">
|
||||
<img src="https://avatars.githubusercontent.com/u/53431819?u=604977bed6ad6875ada890d0d3765a4cacc2fa14&v=4" width="80;" alt="Bastii717"/>
|
||||
@ -112,17 +141,38 @@
|
||||
</a>
|
||||
</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"/>
|
||||
<a href="https://github.com/M2TD">
|
||||
<img src="https://avatars.githubusercontent.com/u/85460457?v=4" width="80;" alt="M2TD"/>
|
||||
<br />
|
||||
<sub><b>Ratty222</b></sub>
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/CrazyWolf13">
|
||||
<img src="https://avatars.githubusercontent.com/u/96661824?v=4" width="80;" alt="CrazyWolf13"/>
|
||||
<a href="https://github.com/frankdez93">
|
||||
<img src="https://avatars.githubusercontent.com/u/87549420?v=4" width="80;" alt="frankdez93"/>
|
||||
<br />
|
||||
<sub><b>Tobias</b></sub>
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/terminaltrove">
|
||||
<img src="https://avatars.githubusercontent.com/u/121595180?v=4" width="80;" alt="terminaltrove"/>
|
||||
<br />
|
||||
<sub><b>Terminal Trove</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/NixyJuppie">
|
||||
<img src="https://avatars.githubusercontent.com/u/138570196?u=b102c222487905728b858704962d32759df29ebe&v=4" width="80;" alt="NixyJuppie"/>
|
||||
<br />
|
||||
<sub><b>Nixy</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/nrvo">
|
||||
<img src="https://avatars.githubusercontent.com/u/151435968?u=e1dcb307fd0efdc45cddbe9490a7b956e4da6835&v=4" width="80;" alt="nrvo"/>
|
||||
<br />
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
</table>
|
||||
@ -182,6 +232,13 @@
|
||||
<sub><b>Matthias Bilger</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/CrazyWolf13">
|
||||
<img src="https://avatars.githubusercontent.com/u/96661824?v=4" width="80;" alt="CrazyWolf13"/>
|
||||
<br />
|
||||
<sub><b>Tobias</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/pinarruiz">
|
||||
<img src="https://avatars.githubusercontent.com/u/37040888?v=4" width="80;" alt="pinarruiz"/>
|
||||
@ -189,6 +246,13 @@
|
||||
<sub><b>Alejandro Pinar Ruiz</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/imjimmeh">
|
||||
<img src="https://avatars.githubusercontent.com/u/2104997?v=4" width="80;" alt="imjimmeh"/>
|
||||
<br />
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/kashif-se">
|
||||
<img src="https://avatars.githubusercontent.com/u/5568138?v=4" width="80;" alt="kashif-se"/>
|
||||
@ -196,6 +260,14 @@
|
||||
<sub><b>Kashif Sohail</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/walkxcode">
|
||||
<img src="https://avatars.githubusercontent.com/u/71191962?v=4" width="80;" alt="walkxcode"/>
|
||||
<br />
|
||||
<sub><b>Walkx</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/josuablejeru">
|
||||
<img src="https://avatars.githubusercontent.com/u/37913833?v=4" width="80;" alt="josuablejeru"/>
|
||||
@ -204,32 +276,10 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/walkxcode">
|
||||
<img src="https://avatars.githubusercontent.com/u/71191962?v=4" width="80;" alt="walkxcode"/>
|
||||
<a href="https://github.com/evroon">
|
||||
<img src="https://avatars.githubusercontent.com/u/11857441?v=4" width="80;" alt="evroon"/>
|
||||
<br />
|
||||
<sub><b>Walkx</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/albcp">
|
||||
<img src="https://avatars.githubusercontent.com/u/3170731?v=4" width="80;" alt="albcp"/>
|
||||
<br />
|
||||
<sub><b>Alberto</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Cereal916">
|
||||
<img src="https://avatars.githubusercontent.com/u/7526937?v=4" width="80;" alt="Cereal916"/>
|
||||
<br />
|
||||
<sub><b>Kristian Brasel</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/lordpansar">
|
||||
<img src="https://avatars.githubusercontent.com/u/14231148?v=4" width="80;" alt="lordpansar"/>
|
||||
<br />
|
||||
<sub><b>Magnus Sundström</b></sub>
|
||||
<sub><b>Erik Vroon</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
@ -240,12 +290,27 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/evroon">
|
||||
<img src="https://avatars.githubusercontent.com/u/11857441?v=4" width="80;" alt="evroon"/>
|
||||
<a href="https://github.com/lordpansar">
|
||||
<img src="https://avatars.githubusercontent.com/u/14231148?v=4" width="80;" alt="lordpansar"/>
|
||||
<br />
|
||||
<sub><b>Erik Vroon</b></sub>
|
||||
<sub><b>Magnus Sundström</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Cereal916">
|
||||
<img src="https://avatars.githubusercontent.com/u/7526937?v=4" width="80;" alt="Cereal916"/>
|
||||
<br />
|
||||
<sub><b>Kristian Brasel</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/albcp">
|
||||
<img src="https://avatars.githubusercontent.com/u/3170731?v=4" width="80;" alt="albcp"/>
|
||||
<br />
|
||||
<sub><b>Alberto</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/altearius">
|
||||
<img src="https://avatars.githubusercontent.com/u/270430?v=4" width="80;" alt="altearius"/>
|
||||
@ -259,8 +324,14 @@
|
||||
<br />
|
||||
<sub><b>UrekD</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/z3r0l1nk">
|
||||
<img src="https://avatars.githubusercontent.com/u/31653632?v=4" width="80;" alt="z3r0l1nk"/>
|
||||
<br />
|
||||
<sub><b>Mihai</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/wozboz">
|
||||
<img src="https://avatars.githubusercontent.com/u/51856582?v=4" width="80;" alt="wozboz"/>
|
||||
@ -276,10 +347,18 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/remygrandin">
|
||||
<img src="https://avatars.githubusercontent.com/u/1934515?v=4" width="80;" alt="remygrandin"/>
|
||||
<a href="https://github.com/aviolaris">
|
||||
<img src="https://avatars.githubusercontent.com/u/48277853?v=4" width="80;" alt="aviolaris"/>
|
||||
<br />
|
||||
<sub><b>Remygrandin</b></sub>
|
||||
<sub><b>Andreas Violaris</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Tracreed">
|
||||
<img src="https://avatars.githubusercontent.com/u/6306365?v=4" width="80;" alt="Tracreed"/>
|
||||
<br />
|
||||
<sub><b>David Alasow</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
@ -290,68 +369,17 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Tracreed">
|
||||
<img src="https://avatars.githubusercontent.com/u/6306365?v=4" width="80;" alt="Tracreed"/>
|
||||
<a href="https://github.com/remygrandin">
|
||||
<img src="https://avatars.githubusercontent.com/u/1934515?v=4" width="80;" alt="remygrandin"/>
|
||||
<br />
|
||||
<sub><b>David Alasow</b></sub>
|
||||
<sub><b>Remygrandin</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/aviolaris">
|
||||
<img src="https://avatars.githubusercontent.com/u/48277853?v=4" width="80;" alt="aviolaris"/>
|
||||
<a href="https://github.com/alucarddelta">
|
||||
<img src="https://avatars.githubusercontent.com/u/20882097?v=4" width="80;" alt="alucarddelta"/>
|
||||
<br />
|
||||
<sub><b>Andreas Violaris</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/rubenandre">
|
||||
<img src="https://avatars.githubusercontent.com/u/9402773?v=4" width="80;" alt="rubenandre"/>
|
||||
<br />
|
||||
<sub><b>Rúben Silva</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/rtm516">
|
||||
<img src="https://avatars.githubusercontent.com/u/5401186?v=4" width="80;" alt="rtm516"/>
|
||||
<br />
|
||||
<sub><b>Rtm516</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/zcq100">
|
||||
<img src="https://avatars.githubusercontent.com/u/425234?v=4" width="80;" alt="zcq100"/>
|
||||
<br />
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/onedr0p">
|
||||
<img src="https://avatars.githubusercontent.com/u/213795?v=4" width="80;" alt="onedr0p"/>
|
||||
<br />
|
||||
<sub><b>ᗪєνιη ᗷυнʟ</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/stanly0726">
|
||||
<img src="https://avatars.githubusercontent.com/u/37040069?v=4" width="80;" alt="stanly0726"/>
|
||||
<br />
|
||||
<sub><b>Stanly0726</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Bogyie">
|
||||
<img src="https://avatars.githubusercontent.com/u/82003678?v=4" width="80;" alt="Bogyie"/>
|
||||
<br />
|
||||
<sub><b>Bogyeong Kim</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Tuzi555">
|
||||
<img src="https://avatars.githubusercontent.com/u/62188066?v=4" width="80;" alt="Tuzi555"/>
|
||||
<br />
|
||||
<sub><b>Jakub Tuzar</b></sub>
|
||||
<sub><b>Brent</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
@ -362,12 +390,56 @@
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/alucarddelta">
|
||||
<img src="https://avatars.githubusercontent.com/u/20882097?v=4" width="80;" alt="alucarddelta"/>
|
||||
<a href="https://github.com/Tuzi555">
|
||||
<img src="https://avatars.githubusercontent.com/u/62188066?v=4" width="80;" alt="Tuzi555"/>
|
||||
<br />
|
||||
<sub><b>Brent</b></sub>
|
||||
<sub><b>Jakub Tuzar</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Bogyie">
|
||||
<img src="https://avatars.githubusercontent.com/u/82003678?v=4" width="80;" alt="Bogyie"/>
|
||||
<br />
|
||||
<sub><b>Bogyeong Kim</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/stanly0726">
|
||||
<img src="https://avatars.githubusercontent.com/u/37040069?v=4" width="80;" alt="stanly0726"/>
|
||||
<br />
|
||||
<sub><b>Stanly0726</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/onedr0p">
|
||||
<img src="https://avatars.githubusercontent.com/u/213795?v=4" width="80;" alt="onedr0p"/>
|
||||
<br />
|
||||
<sub><b>ᗪєνιη ᗷυнʟ</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/zcq100">
|
||||
<img src="https://avatars.githubusercontent.com/u/425234?v=4" width="80;" alt="zcq100"/>
|
||||
<br />
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/rtm516">
|
||||
<img src="https://avatars.githubusercontent.com/u/5401186?v=4" width="80;" alt="rtm516"/>
|
||||
<br />
|
||||
<sub><b>Rtm516</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/rubenandre">
|
||||
<img src="https://avatars.githubusercontent.com/u/9402773?v=4" width="80;" alt="rubenandre"/>
|
||||
<br />
|
||||
<sub><b>Rúben Silva</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/k073l">
|
||||
<img src="https://avatars.githubusercontent.com/u/21180271?v=4" width="80;" alt="k073l"/>
|
||||
@ -388,8 +460,7 @@
|
||||
<br />
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/patrickheeney">
|
||||
<img src="https://avatars.githubusercontent.com/u/1407228?v=4" width="80;" alt="patrickheeney"/>
|
||||
@ -410,7 +481,8 @@
|
||||
<br />
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/BySempron">
|
||||
<img src="https://avatars.githubusercontent.com/u/15928132?v=4" width="80;" alt="BySempron"/>
|
||||
@ -431,8 +503,7 @@
|
||||
<br />
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/thomaswienecke">
|
||||
<img src="https://avatars.githubusercontent.com/u/11446531?v=4" width="80;" alt="thomaswienecke"/>
|
||||
@ -453,7 +524,8 @@
|
||||
<br />
|
||||
<sub><b>Dan Gilbert</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/moemoeq">
|
||||
<img src="https://avatars.githubusercontent.com/u/1808434?v=4" width="80;" alt="moemoeq"/>
|
||||
@ -474,8 +546,14 @@
|
||||
<br />
|
||||
<sub><b>Alexander Mnich</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/alayham">
|
||||
<img src="https://avatars.githubusercontent.com/u/518776?v=4" width="80;" alt="alayham"/>
|
||||
<br />
|
||||
<sub><b>Al-Ayham Saleh</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"/>
|
||||
@ -483,6 +561,14 @@
|
||||
<sub><b>Alessandro Del Prete</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/sachahjkl">
|
||||
<img src="https://avatars.githubusercontent.com/u/32895534?v=4" width="80;" alt="sachahjkl"/>
|
||||
<br />
|
||||
<sub><b>Sacha</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/shazzx">
|
||||
<img src="https://avatars.githubusercontent.com/u/131521332?v=4" width="80;" alt="shazzx"/>
|
||||
@ -517,15 +603,15 @@
|
||||
<br />
|
||||
<sub><b>Stavros Kois</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/XenonR">
|
||||
<img src="https://avatars.githubusercontent.com/u/18627623?v=4" width="80;" alt="XenonR"/>
|
||||
<br />
|
||||
<sub><b>Steffen Schmidt</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/StevKast">
|
||||
<img src="https://avatars.githubusercontent.com/u/17804308?v=4" width="80;" alt="StevKast"/>
|
||||
@ -533,13 +619,6 @@
|
||||
<sub><b>Steven Kast</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/CrazyWolf13">
|
||||
<img src="https://avatars.githubusercontent.com/u/96661824?v=4" width="80;" alt="CrazyWolf13"/>
|
||||
<br />
|
||||
<sub><b>Tobias</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/AmadeusGraves">
|
||||
<img src="https://avatars.githubusercontent.com/u/18572939?v=4" width="80;" alt="AmadeusGraves"/>
|
||||
@ -547,21 +626,13 @@
|
||||
<sub><b>Ángel Fernández Sánchez</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/sachahjkl">
|
||||
<img src="https://avatars.githubusercontent.com/u/32895534?v=4" width="80;" alt="sachahjkl"/>
|
||||
<br />
|
||||
<sub><b>Sacha</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/turnrye">
|
||||
<img src="https://avatars.githubusercontent.com/u/701035?v=4" width="80;" alt="turnrye"/>
|
||||
<br />
|
||||
<sub><b>Ryan Turner</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/rubjo">
|
||||
<img src="https://avatars.githubusercontent.com/u/42270947?v=4" width="80;" alt="rubjo"/>
|
||||
@ -582,7 +653,8 @@
|
||||
<br />
|
||||
<sub><b>Andrey</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/oka4shi">
|
||||
<img src="https://avatars.githubusercontent.com/u/67847553?v=4" width="80;" alt="oka4shi"/>
|
||||
@ -603,15 +675,29 @@
|
||||
<br />
|
||||
<sub><b>Michael Lavaire</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/imsakg">
|
||||
<img src="https://avatars.githubusercontent.com/u/62212589?v=4" width="80;" alt="imsakg"/>
|
||||
<br />
|
||||
<sub><b>Mert Sefa AKGUN</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/ethan-hann">
|
||||
<img src="https://avatars.githubusercontent.com/u/36464732?v=4" width="80;" alt="ethan-hann"/>
|
||||
<br />
|
||||
<sub><b>Ethan Hann</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/tazboyz16">
|
||||
<img src="https://avatars.githubusercontent.com/u/12215340?v=4" width="80;" alt="tazboyz16"/>
|
||||
<br />
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/sur1v">
|
||||
<img src="https://avatars.githubusercontent.com/u/19678230?v=4" width="80;" alt="sur1v"/>
|
||||
@ -633,6 +719,13 @@
|
||||
<sub><b>Null</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/markxoe">
|
||||
<img src="https://avatars.githubusercontent.com/u/59475466?v=4" width="80;" alt="markxoe"/>
|
||||
<br />
|
||||
<sub><b>Mark Oude Elberink</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/lxjv">
|
||||
<img src="https://avatars.githubusercontent.com/u/63261955?v=4" width="80;" alt="lxjv"/>
|
||||
@ -719,21 +812,6 @@
|
||||
<sub><b>Xert</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/FormatToday">
|
||||
<img src="https://avatars.githubusercontent.com/u/20515769?v=4" width="80;" alt="FormatToday"/>
|
||||
<br />
|
||||
<sub><b>FormatToday</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/ethan-hann">
|
||||
<img src="https://avatars.githubusercontent.com/u/36464732?v=4" width="80;" alt="ethan-hann"/>
|
||||
<br />
|
||||
<sub><b>Ethan Hann</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/emiran-orange">
|
||||
<img src="https://avatars.githubusercontent.com/u/71817149?v=4" width="80;" alt="emiran-orange"/>
|
||||
@ -747,7 +825,8 @@
|
||||
<br />
|
||||
<sub><b>Eduardo Gomez</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Dylan-Bs">
|
||||
<img src="https://avatars.githubusercontent.com/u/35694107?v=4" width="80;" alt="Dylan-Bs"/>
|
||||
@ -775,8 +854,7 @@
|
||||
<br />
|
||||
<sub><b>DeepSource Bot</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/DawidPietrykowski">
|
||||
<img src="https://avatars.githubusercontent.com/u/53954695?v=4" width="80;" alt="DawidPietrykowski"/>
|
||||
@ -790,6 +868,14 @@
|
||||
<br />
|
||||
<sub><b>David</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/clsty">
|
||||
<img src="https://avatars.githubusercontent.com/u/129247596?v=4" width="80;" alt="clsty"/>
|
||||
<br />
|
||||
<sub><b>Celestial.y</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/bskim45">
|
||||
@ -818,14 +904,21 @@
|
||||
<br />
|
||||
<sub><b>Jyotirmoy Bandyopadhyaya [Bravo68]</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/AaronPorts">
|
||||
<img src="https://avatars.githubusercontent.com/u/32810520?v=4" width="80;" alt="AaronPorts"/>
|
||||
<br />
|
||||
<sub><b>Artyom</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/alydemah">
|
||||
<img src="https://avatars.githubusercontent.com/u/652035?v=4" width="80;" alt="alydemah"/>
|
||||
<br />
|
||||
<sub><b>Aly Mohamed</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/5idereal">
|
||||
@ -841,13 +934,6 @@
|
||||
<sub><b>0n1cOn3</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/imsakg">
|
||||
<img src="https://avatars.githubusercontent.com/u/62212589?v=4" width="80;" alt="imsakg"/>
|
||||
<br />
|
||||
<sub><b>Mert Sefa AKGUN</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/maximemoreillon">
|
||||
<img src="https://avatars.githubusercontent.com/u/29086128?v=4" width="80;" alt="maximemoreillon"/>
|
||||
@ -861,15 +947,15 @@
|
||||
<br />
|
||||
<sub><b>Max Kulik</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/markusdd">
|
||||
<img src="https://avatars.githubusercontent.com/u/25175069?v=4" width="80;" alt="markusdd"/>
|
||||
<br />
|
||||
<sub><b>Markus Krause</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/asenov">
|
||||
<img src="https://avatars.githubusercontent.com/u/280619?v=4" width="80;" alt="asenov"/>
|
||||
@ -904,15 +990,15 @@
|
||||
<br />
|
||||
<sub><b>Kieran</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></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Hellhium">
|
||||
<img src="https://avatars.githubusercontent.com/u/11504877?v=4" width="80;" alt="Hellhium"/>
|
||||
@ -947,6 +1033,13 @@
|
||||
<br />
|
||||
<sub><b>Ian Neal</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/FraglyG">
|
||||
<img src="https://avatars.githubusercontent.com/u/56320839?v=4" width="80;" alt="FraglyG"/>
|
||||
<br />
|
||||
<sub><b>Hendrik Strydom</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
@ -962,6 +1055,13 @@
|
||||
<br />
|
||||
<sub><b>Garrett Brown</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/FormatToday">
|
||||
<img src="https://avatars.githubusercontent.com/u/20515769?v=4" width="80;" alt="FormatToday"/>
|
||||
<br />
|
||||
<sub><b>FormatToday</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
</table>
|
||||
<!-- readme: contributors -end -->
|
||||
|
@ -21,6 +21,7 @@ Once you've got Dashy up and running, you'll want to configure it with your own
|
||||
- [Deployment Methods](#deployment-methods)
|
||||
- [Deploy with Docker](#deploy-with-docker)
|
||||
- [Using Docker Compose](#using-docker-compose)
|
||||
- [Kubernetes](#kubernetes)
|
||||
- [Unraid](#unraid)
|
||||
- [Synology NAS](#synology-nas)
|
||||
- [Build from Source](#build-from-source)
|
||||
@ -32,6 +33,7 @@ Once you've got Dashy up and running, you'll want to configure it with your own
|
||||
- [Google Cloud Platform](#google-cloud-platform)
|
||||
- [Platform.sh](#platformsh)
|
||||
- [Render](#render)
|
||||
- [Railway](#railway)
|
||||
- [Scalingo](#scalingo)
|
||||
- [Play-with-Docker](#play-with-docker)
|
||||
- [Surge.sh](#surgesh)
|
||||
@ -65,8 +67,8 @@ Dashy has a built container image hosted on [Docker Hub](https://hub.docker.com/
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-p 8080:80 \
|
||||
-v /root/my-local-conf.yml:/app/public/conf.yml \
|
||||
-p 8080:8080 \
|
||||
-v /root/my-local-conf.yml:/app/user-data/conf.yml \
|
||||
--name my-dashboard \
|
||||
--restart=always \
|
||||
lissy93/dashy:latest
|
||||
@ -108,9 +110,9 @@ 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:/app/user-data/conf.yml
|
||||
ports:
|
||||
- 4000:80
|
||||
- 4000:8080
|
||||
# Set any environmental variables
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
@ -134,6 +136,12 @@ If you are building from source, and would like to use one of the [other Dockerf
|
||||
|
||||
---
|
||||
|
||||
## Kubernetes
|
||||
|
||||
@vyrtualsynthese has written a Helm Chart for deploying with Kubernetes, available [here](https://github.com/vyrtualsynthese/selfhosted-helmcharts/tree/main/charts/dashy)
|
||||
|
||||
---
|
||||
|
||||
## Unraid
|
||||
|
||||
// TODO
|
||||
@ -158,8 +166,8 @@ Installing dashy is really simply and fast:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-p 4000:80 \
|
||||
-v /volume1/docker/dashy/my-local-conf.yml:/app/public/conf.yml \
|
||||
-p 4000:8080 \
|
||||
-v /volume1/docker/dashy/my-local-conf.yml:/app/user-data/conf.yml \
|
||||
--name dashy \
|
||||
--restart=always \
|
||||
lissy93/dashy:latest
|
||||
@ -174,7 +182,7 @@ dashy should be up within 1-2min after you've started the install task procedure
|
||||
If you do not want to use Docker, you can run Dashy directly on your host system. For this, you will need both [git](https://git-scm.com/downloads) and the latest or LTS version of [Node.js](https://nodejs.org/) installed, and optionally [yarn](https://yarnpkg.com/)
|
||||
|
||||
1. Get Code: `git clone https://github.com/Lissy93/dashy.git` and `cd dashy`
|
||||
2. Configuration: Fill in you're settings in `./public/conf.yml`
|
||||
2. Configuration: Fill in you're settings in `./user-data/conf.yml`
|
||||
3. Install dependencies: `yarn`
|
||||
4. Build: `yarn build`
|
||||
5. Run: `yarn start`
|
||||
@ -187,7 +195,8 @@ If you don't have a home server, then fear not - Dashy can be deployed to pretty
|
||||
|
||||
Some hosting providers required a bit of extra configuration, which was why I've made separate branches for deploying to those services (named: [`deploy_cloudflare`](https://github.com/Lissy93/dashy/tree/deploy_cloudflare), [`deploy_digital-ocean`](https://github.com/Lissy93/dashy/tree/deploy_digital-ocean), [`deploy_platform-sh`](https://github.com/Lissy93/dashy/tree/deploy_platform-sh) and [`deploy_render`](https://github.com/Lissy93/dashy/tree/deploy_render)). If there's another cloud service which you'd like 1-click deployment to be supported for, feel free to raise an issue.
|
||||
|
||||
**Note** If you use a static hosting provider, then status checks, writing new config changes to disk from the UI, and triggering a rebuild through the UI will not be available. This is because these features need endpoints provided by Dashy's local Node server. Everything else should work just the same though.
|
||||
> [!NOTE]
|
||||
> If you use a static hosting provider, then status checks, writing new config changes to disk from the UI, and triggering a rebuild through the UI will not be available. This is because these features need endpoints provided by Dashy's local Node server. Everything else should work just the same though.
|
||||
|
||||
### Netlify
|
||||
|
||||
@ -271,6 +280,16 @@ To deploy Dashy to Render, use the following link
|
||||
https://render.com/deploy?repo=https://github.com/lissy93/dashy/tree/deploy_render
|
||||
```
|
||||
|
||||
### Railway
|
||||
|
||||
[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/template/MtdjAQ?referralCode=app)
|
||||
|
||||
[Railway](https://railway.app/) is a Platform as a Service (PaaS) that offers a complete platform for building and delivering programs to the backend of the cloud. You bring your code and Railway does the rest. Railway offers an extremely good developer experience and makes it effortless to deploy apps. Railway offers a free Trial Plan, and paid plans start at $5/month. Railway has 4 server locations: US-west, US-east, EU-west and ASIA-South-East.
|
||||
|
||||
```text
|
||||
https://railway.app/template/MtdjAQ
|
||||
```
|
||||
|
||||
### Scalingo
|
||||
|
||||
[![Deploy on Scalingo](https://i.ibb.co/nj0KxyH/deploy-scalingo-button.png)](https://my.scalingo.com/deploy?source=https://github.com/lissy93/dashy#master)
|
||||
|
@ -51,7 +51,7 @@ Dashy should now be being served on <http://localhost:8080/>. Hot reload is enab
|
||||
|
||||
#### Utils and Checks
|
||||
|
||||
- **`yarn validate-config`** - If you have quite a long configuration file, you may wish to check that it's all good to go, before deploying the app. This can be done with `yarn validate-config` or `docker exec -it [container-id] yarn validate-config`. Your config file needs to be in `/public/conf.yml` (or within your Docker container at `/app/public/conf.yml`). This will first check that your YAML is valid, and then validates it against Dashy's [schema](https://github.com/Lissy93/dashy/blob/master/src/utils/ConfigSchema.js).
|
||||
- **`yarn validate-config`** - If you have quite a long configuration file, you may wish to check that it's all good to go, before deploying the app. This can be done with `yarn validate-config` or `docker exec -it [container-id] yarn validate-config`. Your config file needs to be in `/user-data/conf.yml` (or within your Docker container at `/app/user-data/conf.yml`). This will first check that your YAML is valid, and then validates it against Dashy's [schema](https://github.com/Lissy93/dashy/blob/master/src/utils/ConfigSchema.js).
|
||||
- **`yarn health-check`** - Checks that the application is up and running on it's specified port, and outputs current status and response times. Useful for integrating into your monitoring service, if you need to maintain high system availability
|
||||
|
||||
#### Alternate Start Commands
|
||||
|
@ -104,7 +104,7 @@ If you are not comfortable with making pull requests, or do not want to modify t
|
||||
|
||||
This section is for, adding a new setting to the config file.
|
||||
|
||||
All of the users config is specified in `./public/conf.yml` - see [Configuring Docs](./configuring.md) for info.
|
||||
All of the users config is specified in `./user-data/conf.yml` - see [Configuring Docs](./configuring.md) for info.
|
||||
It's important to first ensure that there isn't a similar option already available, the new option is definitely necessary, and most importantly that it is fully backwards compatible.
|
||||
|
||||
Next choose the appropriate section to place it under
|
||||
|
@ -30,11 +30,11 @@ _The following article is a primer on managing self-hosted apps. It covers every
|
||||
|
||||
Although not essential, you will most likely want to provide several assets to your running app.
|
||||
|
||||
This is easy to do using [Docker Volumes](https://docs.docker.com/storage/volumes/), which lets you share a file or directory between your host system, and the container. Volumes are specified in the Docker run command, or Docker compose file, using the `--volume` or `-v` flags. The value of which consists of the path to the file / directory on your host system, followed by the destination path within the container. Fields are separated by a colon (`:`), and must be in the correct order. For example: `-v ~/alicia/my-local-conf.yml:/app/public/conf.yml`
|
||||
This is easy to do using [Docker Volumes](https://docs.docker.com/storage/volumes/), which lets you share a file or directory between your host system, and the container. Volumes are specified in the Docker run command, or Docker compose file, using the `--volume` or `-v` flags. The value of which consists of the path to the file / directory on your host system, followed by the destination path within the container. Fields are separated by a colon (`:`), and must be in the correct order. For example: `-v ~/alicia/my-local-conf.yml:/app/user-data/conf.yml`
|
||||
|
||||
In Dashy, commonly configured resources include:
|
||||
|
||||
- `./public/conf.yml` - Your main application config file
|
||||
- `./user-data/conf.yml` - Your main application config file
|
||||
- `./public/item-icons` - A directory containing your own icons. This allows for offline access, and better performance than fetching from a CDN
|
||||
- Also within `./public` you'll find standard website assets, including `favicon.ico`, `manifest.json`, `robots.txt`, etc. There's no need to pass these in, but you can do so if you wish
|
||||
- `/src/styles/user-defined-themes.scss` - A stylesheet for applying custom CSS to your app. You can also write your own themes here.
|
||||
@ -238,7 +238,7 @@ Once you've generated your SSL cert, you'll need to pass it to Dashy. This can b
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-p 8080:80 \
|
||||
-p 8080:8080 \
|
||||
-v ~/my-private-key.key:/etc/ssl/certs/dashy-priv.key:ro \
|
||||
-v ~/my-public-key.pem:/etc/ssl/certs/dashy-pub.pem:ro \
|
||||
lissy93/dashy:latest
|
||||
@ -276,9 +276,9 @@ services:
|
||||
container_name: Dashy
|
||||
image: lissy93/dashy
|
||||
volumes:
|
||||
- /root/my-config.yml:/app/public/conf.yml
|
||||
- /root/my-config.yml:/app/user-data/conf.yml
|
||||
ports:
|
||||
- 4000:80
|
||||
- 4000:8080
|
||||
environment:
|
||||
- BASE_URL=/my-dashboard
|
||||
restart: unless-stopped
|
||||
@ -550,7 +550,7 @@ upstream dashy {
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
listen 8080;
|
||||
server_name dashy.mydomain.com;
|
||||
|
||||
# Setup SSL
|
||||
@ -577,7 +577,7 @@ Similarly, a basic `Caddyfile` might look like:
|
||||
|
||||
```text
|
||||
dashy.example.com {
|
||||
reverse_proxy / nginx:80
|
||||
reverse_proxy / nginx:8080
|
||||
}
|
||||
```
|
||||
|
||||
@ -614,7 +614,7 @@ To prevent known container escape vulnerabilities, which typically end in escala
|
||||
Docker enables you to limit resource consumption (CPU, memory, disk) on a per-container basis. This not only enhances system performance, but also prevents a compromised container from consuming a large amount of resources, in order to disrupt service or perform malicious activities. To learn more, see the [Resource Constraints Docs](https://docs.docker.com/config/containers/resource_constraints/)
|
||||
|
||||
For example, to run Dashy with max of 1GB ram, and max of 50% of 1 CP core:
|
||||
`docker run -d -p 8080:80 --cpus=".5" --memory="1024m" lissy93/dashy:latest`
|
||||
`docker run -d -p 8080:8080 --cpus=".5" --memory="1024m" lissy93/dashy:latest`
|
||||
|
||||
### Don't Run as Root
|
||||
|
||||
@ -629,7 +629,7 @@ One of the best ways to prevent privilege escalation attacks, is to configure th
|
||||
You can specify a user, using the [`--user` param](https://docs.docker.com/engine/reference/run/#user), and should include the user ID (`UID`), which can be found by running `id -u`, and the and the group ID (`GID`), using `id -g`.
|
||||
|
||||
With Docker run, you specify it like:
|
||||
`docker run --user 1000:1000 -p 8080:80 lissy93/dashy`
|
||||
`docker run --user 1000:1000 -p 8080:8080 lissy93/dashy`
|
||||
|
||||
Of if you're using Docker-compose, you could use an environmental variable
|
||||
|
||||
@ -639,7 +639,7 @@ services:
|
||||
dashy:
|
||||
image: lissy93/dashy
|
||||
user: ${CURRENT_UID}
|
||||
ports: [ 4000:80 ]
|
||||
ports: [ 4000:8080 ]
|
||||
```
|
||||
|
||||
And then to set the variable, and start the container, run: `CURRENT_UID=$(id -u):$(id -g) docker-compose up`
|
||||
@ -659,7 +659,7 @@ version: "3.8"
|
||||
services:
|
||||
dashy:
|
||||
image: lissy93/dashy
|
||||
ports: [ 4000:80 ]
|
||||
ports: [ 4000:8080 ]
|
||||
cap_drop:
|
||||
- ALL
|
||||
cap_add:
|
||||
@ -675,7 +675,7 @@ services:
|
||||
To prevent processes inside the container from getting additional privileges, pass in the `--security-opt=no-new-privileges:true` option to the Docker run command (see [docs](https://docs.docker.com/engine/reference/run/#security-configuration)).
|
||||
|
||||
Run Command:
|
||||
`docker run --security-opt=no-new-privileges:true -p 8080:80 lissy93/dashy`
|
||||
`docker run --security-opt=no-new-privileges:true -p 8080:8080 lissy93/dashy`
|
||||
|
||||
Docker Compose
|
||||
|
||||
@ -701,14 +701,14 @@ You can specify that a specific volume should be read-only by appending `:ro` to
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-p 8080:80 \
|
||||
-v ~/dashy-conf.yml:/app/public/conf.yml \
|
||||
-p 8080:8080 \
|
||||
-v ~/dashy-conf.yml:/app/user-data/conf.yml \
|
||||
-v ~/dashy-icons:/app/public/item-icons:ro \
|
||||
-v ~/dashy-theme.scss:/app/src/styles/user-defined-themes.scss:ro \
|
||||
lissy93/dashy:latest
|
||||
```
|
||||
|
||||
You can also prevent a container from writing any changes to volumes on your host's disk, using the `--read-only` flag. Although, for Dashy, you will not be able to write config changes to disk, when edited through the UI with this method. You could make this work, by specifying the config directory as a temp write location, with `--tmpfs /app/public/conf.yml` - but that this will not write the volume back to your host.
|
||||
You can also prevent a container from writing any changes to volumes on your host's disk, using the `--read-only` flag. Although, for Dashy, you will not be able to write config changes to disk, when edited through the UI with this method. You could make this work, by specifying the config directory as a temp write location, with `--tmpfs /app/user-data/conf.yml` - but that this will not write the volume back to your host.
|
||||
|
||||
### Set the Logging Level
|
||||
|
||||
@ -778,8 +778,8 @@ Create a new file in `/etc/nginx/sites-enabled/dashy`
|
||||
|
||||
```text
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
listen 8080;
|
||||
listen [::]:8080;
|
||||
|
||||
root /var/www/dashy/html;
|
||||
index index.html;
|
||||
@ -898,7 +898,7 @@ Similar to above, you'll first need to fork and clone Dashy to your local system
|
||||
|
||||
Then, either use Dashy's default [`Dockerfile`](https://github.com/Lissy93/dashy/blob/master/Dockerfile) as is, or modify it according to your needs.
|
||||
|
||||
To build and deploy locally, first build the app with: `docker build -t dashy .`, and then start the app with `docker run -p 8080:80 --name my-dashboard dashy`. Or modify the `docker-compose.yml` file, replacing `image: lissy93/dashy` with `build: .` and run `docker compose up`.
|
||||
To build and deploy locally, first build the app with: `docker build -t dashy .`, and then start the app with `docker run -p 8080:8080 --name my-dashboard dashy`. Or modify the `docker-compose.yml` file, replacing `image: lissy93/dashy` with `build: .` and run `docker compose up`.
|
||||
|
||||
Your container should now be running, and will appear in the list when you run `docker container ls –a`. If you'd like to enter the container, run `docker exec -it [container-id] /bin/ash`.
|
||||
|
||||
|
@ -20,7 +20,7 @@ To pull the latest image, and build and start the app run:
|
||||
```bash
|
||||
docker run -d \
|
||||
-p 8080:80 \
|
||||
-v ~/my-conf.yml:/app/public/conf.yml \
|
||||
-v ~/my-conf.yml:/app/user-data/conf.yml \
|
||||
--name my-dashboard \
|
||||
--restart=always \
|
||||
lissy93/dashy:latest
|
||||
@ -35,7 +35,7 @@ Your dashboard should now be up and running at `http://localhost:8080` (or your
|
||||
## 3. Configure
|
||||
|
||||
Now that you've got Dashy running, you are going to want to set it up with your own content.
|
||||
Config is written in [YAML Format](https://yaml.org/), and saved in [`/public/conf.yml`](https://github.com/Lissy93/dashy/blob/master/public/conf.yml).
|
||||
Config is written in [YAML Format](https://yaml.org/), and saved in [`/user-data/conf.yml`](https://github.com/Lissy93/dashy/blob/master/user-data/conf.yml).
|
||||
The format on the config file is pretty straight forward. There are three root attributes:
|
||||
|
||||
- [`pageInfo`](https://github.com/Lissy93/dashy/blob/master/docs/configuring.md#pageinfo) - Dashboard meta data, like title, description, nav bar links and footer text
|
||||
@ -72,7 +72,7 @@ sections: # An array of sections
|
||||
Notes:
|
||||
|
||||
- You can use a Docker volume to pass a config file from your host system to the container
|
||||
- E.g. `-v ./host-system/my-local-conf.yml:/app/public/conf.yml`
|
||||
- E.g. `-v ./host-system/my-local-conf.yml:/app/user-data/conf.yml`
|
||||
- It's also possible to edit your config directly through the UI, and changes will be saved in this file
|
||||
- Check your config against Dashy's schema, with `docker exec -it [container-id] yarn validate-config`
|
||||
- You might find it helpful to look at some examples, a collection of which can be [found here](https://gist.github.com/Lissy93/000f712a5ce98f212817d20bc16bab10)
|
||||
@ -118,7 +118,7 @@ yarn build # Build the app
|
||||
yarn start # Start the app
|
||||
```
|
||||
|
||||
Then edit `./public/conf.yml` and rebuild the app with `yarn build`
|
||||
Then edit `./user-data/conf.yml` and rebuild the app with `yarn build`
|
||||
|
||||
---
|
||||
|
||||
@ -129,7 +129,7 @@ Don't have a server? No problem! You can run Dashy for free on Netlify (as well
|
||||
1. Fork Dashy's repository on GitHub
|
||||
2. [Log in](app.netlify.com/login/) to Netlify with GitHub
|
||||
3. Click "New site from Git" and select your forked repo, then click **Deploy**!
|
||||
4. You can then edit the config in `./public/conf.yml` in your repo, and Netlify will rebuild the app
|
||||
4. You can then edit the config in `./user-data/conf.yml` in your repo, and Netlify will rebuild the app
|
||||
|
||||
---
|
||||
|
||||
|
@ -3,6 +3,14 @@
|
||||
| 💗 Got a sweet dashboard? Submit it to the showcase! 👉 [See How](#submitting-your-dashboard) |
|
||||
|-|
|
||||
|
||||
## MNDashboard
|
||||
|
||||
> By [@mahrnet](https://github.com/mahrnet) <sup>Re: [#1491](https://github.com/Lissy93/dashy/issues/1491)</sup>
|
||||
|
||||
![screenshot-MNDashboard](https://i.ibb.co/mCJRZgp/d2-At-QO4c-PT4u.png)
|
||||
|
||||
---
|
||||
|
||||
## Home Lab 2.0
|
||||
|
||||
![screenshot-homelab](https://raw.githubusercontent.com/Lissy93/dashy/master/docs/showcase/1-home-lab-material.png)
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
## Contents
|
||||
|
||||
- [Config not saving](#config-not-saving)
|
||||
- [Refused to Connect in Web Content View](#refused-to-connect-in-modal-or-workspace-view)
|
||||
- [404 On Static Hosting](#404-on-static-hosting)
|
||||
- [404 from Mobile Home Screen](#404-after-launch-from-mobile-home-screen)
|
||||
@ -18,6 +19,7 @@
|
||||
- [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 Saving on Vercel / Netlify / CDN](#user-content-config-not-saving-on-vercel--netlify--cdn)
|
||||
- [Config Not Updating](#config-not-updating)
|
||||
- [Config Still not Updating](#config-still-not-updating)
|
||||
- [Styles and Assets not Updating](#styles-and-assets-not-updating)
|
||||
@ -45,6 +47,25 @@
|
||||
|
||||
---
|
||||
|
||||
## Config not saving
|
||||
|
||||
### Possible Issue 1: Unable to call save endpoint from CDN/static server
|
||||
If you're running Dashy using a static hosting provider (like Vercel), then there is no Node server, and so the save config action will not work via the UI.
|
||||
You'll instead need to copy the YAML after making your changes, and paste that into your `conf.yml` directly. If you've connected Vercel to git, then these changes will take effect automatically, once you commit your changes.
|
||||
Look here for more information: [https://dashy.to/docs/deployment#deploy-to-cloud-service](https://dashy.to/docs/deployment#deploy-to-cloud-service)
|
||||
|
||||
If you're running on Netlify, there are some cloud functions which take care of all the server endpoints (like status checking), so these will work as expected.
|
||||
|
||||
See also [#1465](https://github.com/Lissy93/dashy/issues/1465)
|
||||
|
||||
### Possible Issue 2: Unable to save
|
||||
In Docker, double check that the file isn't read-only, and that the container actually has permissions to modify it. You shouldn't really be running it as a root user, and I'm not sure if it will work if you do-
|
||||
|
||||
### Possible Issue 3: Saved but not updating
|
||||
After saving, the frontend will recompile, which may take a couple seconds (or a bit longer on a Pi or low-powered device). If it doesn't recompile, you can manually trigger a re-build.
|
||||
|
||||
---
|
||||
|
||||
## `Refused to Connect` in Modal or Workspace View
|
||||
|
||||
This is not an issue with Dashy, but instead caused by the target app preventing direct access through embedded elements.
|
||||
@ -213,7 +234,7 @@ Version 2.0.4 introduced changes to how the config is read, and the app is build
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- /srv/dashy/conf.yml:/app/public/conf.yml
|
||||
- /srv/dashy/conf.yml:/app/user-data/conf.yml
|
||||
- /srv/dashy/item-icons:/app/public/item-icons
|
||||
```
|
||||
|
||||
@ -252,12 +273,23 @@ See also: #479, #409, #507, #491, #341, #520
|
||||
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:
|
||||
"/app/user-data/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`
|
||||
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/user-data/conf.yml`
|
||||
|
||||
---
|
||||
|
||||
## Config not Saving on Vercel / Netlify / CDN
|
||||
|
||||
If you're running Dashy using a static hosting provider (like Vercel), then there is no Node server, and so the save config action will not work via the UI.
|
||||
You'll instead need to copy the YAML after making your changes, and paste that into your `conf.yml` directly. If you've connected Vercel to git, then these changes will take effect automatically, once you commit your changes.
|
||||
|
||||
If you're running on Netlify, there are some cloud functions which take care of all the server endpoints (like status checking), so these will work as expected.
|
||||
|
||||
See also [#1465](https://github.com/Lissy93/dashy/issues/1465)
|
||||
|
||||
---
|
||||
|
||||
@ -537,8 +569,7 @@ For example:
|
||||
export NODE_OPTIONS=--openssl-legacy-provider
|
||||
```
|
||||
|
||||
For more info, see [webpack/webpack#14532](https://github.com/webpack/webpack/issues/14532) and [nodejs/node#40455](https://github.com/nodejs/node/issues/40455).
|
||||
This occours because [Node 17+](https://medium.com/the-node-js-collection/node-js-17-is-here-8dba1e14e382) no longer supports MD4 as hash function, we're in the process of upgrading Dashy dependencies to all use SHA1 for hashing bundle IDs.
|
||||
This will be fixed once [webpack/webpack#17659](https://github.com/webpack/webpack/pull/17659) is merged.
|
||||
|
||||
---
|
||||
|
||||
|
@ -66,6 +66,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a
|
||||
- [Gluetun VPN Info](#gluetun-vpn-info)
|
||||
- [Drone CI Build](#drone-ci-builds)
|
||||
- [Linkding](#linkding)
|
||||
- [Uptime Kuma](#uptime-kuma)
|
||||
- **[System Resource Monitoring](#system-resource-monitoring)**
|
||||
- [CPU Usage Current](#current-cpu-usage)
|
||||
- [CPU Usage Per Core](#cpu-usage-per-core)
|
||||
@ -703,6 +704,8 @@ Display current FX rates in your native currency. Hover over a row to view more
|
||||
|
||||
Counting down to the next day off work? This widget displays upcoming public holidays for your country. Data is fetched from [Enrico](http://kayaposoft.com/enrico/)
|
||||
|
||||
Note, config for this widget is case-sensetive (see [#1268](https://github.com/Lissy93/dashy/issues/1268))
|
||||
|
||||
<p align="center"><img width="400" src="https://i.ibb.co/VC6fZqn/public-holidays.png" /></p>
|
||||
|
||||
#### Options
|
||||
@ -2124,7 +2127,9 @@ This will show the list of nodes.
|
||||
token_name: dashy
|
||||
token_uuid: bfb152df-abcd-abcd-abcd-ccb95a472d01
|
||||
```
|
||||
|
||||
This will show the list of VMs, with a title and a linked fotter, hiding VM templates.
|
||||
|
||||
```yaml
|
||||
- type: proxmox-lists
|
||||
useProxy: true
|
||||
@ -2141,6 +2146,7 @@ This will show the list of VMs, with a title and a linked fotter, hiding VM temp
|
||||
footer_as_link: true
|
||||
hide_templates: 1
|
||||
```
|
||||
|
||||
#### Info
|
||||
|
||||
- **CORS**: 🟠 Proxied
|
||||
@ -2149,6 +2155,12 @@ This will show the list of VMs, with a title and a linked fotter, hiding VM temp
|
||||
- **Host**: Self-Hosted (see [Proxmox Virtual Environment](https://proxmox.com/en/proxmox-ve))
|
||||
- **Privacy**: _See [Proxmox's Privacy Policy](https://proxmox.com/en/privacy-policy)_
|
||||
|
||||
#### Troubleshooting
|
||||
- **404 Error in development mode**: The error might disappear in production mode `yarn start`
|
||||
- **500 Error in production mode**: Try adding the certificate authority (CA) certificate of your Proxmox host to Node.js.
|
||||
- Download the Proxmox CA certificate to your Dashy host.
|
||||
- Export environment variable `NODE_EXTRA_CA_CERTS` and set its value to the path of the downloaded CA certificate. Example: `export NODE_EXTRA_CA_CERTS=/usr/local/share/ca-certificates/devlab_ca.pem`
|
||||
|
||||
---
|
||||
|
||||
### Sabnzbd
|
||||
@ -2231,7 +2243,7 @@ Display the last builds from a [Drone CI](https://www.drone.ci) instance. A self
|
||||
**Field** | **Type** | **Required** | **Description**
|
||||
--- | --- | --- | ---
|
||||
**`host`** | `string` | Required | The hostname of the Drone CI instance.
|
||||
**`apiKey`** | `string` | Required | The API key (https://<your-drone-instance>/account).
|
||||
**`apiKey`** | `string` | Required | The API key (https://[your-drone-instance]/account).
|
||||
**`limit`** | `integer` | _Optional_ | Limit the amounts of listed builds.
|
||||
**`repo`** | `string` | _Optional_ | Show only builds of the specified repo
|
||||
|
||||
@ -2265,7 +2277,7 @@ Linkding is a self-hosted bookmarking service, which has a clean interface and i
|
||||
**Field** | **Type** | **Required** | **Description**
|
||||
--- | --- | --- | ---
|
||||
**`host`** | `string` | Required | The hostname of the Drone CI instance.
|
||||
**`apiKey`** | `string` | Required | The API key (https://<your-linkding-instance>/settings/integrations).
|
||||
**`apiKey`** | `string` | Required | The API key (https://your-linkding-instance/settings/integrations).
|
||||
**`tags`** | `list of string` | _Optional_ | Filter the links by tag.
|
||||
|
||||
#### Example
|
||||
@ -2291,6 +2303,37 @@ Linkding is a self-hosted bookmarking service, which has a clean interface and i
|
||||
|
||||
---
|
||||
|
||||
### Uptime Kuma
|
||||
|
||||
[Uptime Kuma](https://github.com/louislam/uptime-kuma) is an easy-to-use self-hosted monitoring tool.
|
||||
|
||||
#### Options
|
||||
|
||||
| **Field** | **Type** | **Required** | **Description** |
|
||||
| ------------ | -------- | ------------ | ------------------------------------------------------------------------ |
|
||||
| **`url`** | `string` | Required | The URL of the Uptime Kuma instance |
|
||||
| **`apiKey`** | `string` | Required | The API key (see https://github.com/louislam/uptime-kuma/wiki/API-Keys). |
|
||||
|
||||
#### Example
|
||||
|
||||
```yaml
|
||||
- type: uptime-kuma
|
||||
useProxy: true
|
||||
options:
|
||||
apiKey: uk2_99H0Yd3I2pPNIRfn0TqBFu4g5q85R1Mh75yZzw6H
|
||||
url: http://192.168.1.106:3691/metrics
|
||||
```
|
||||
|
||||
#### Info
|
||||
|
||||
- **CORS**: 🟢 Enabled
|
||||
- **Auth**: 🟢 Required
|
||||
- **Price**: 🟢 Free
|
||||
- **Host**: Self-Hosted (see [Uptime Kuma](https://github.com/louislam/uptime-kuma) )
|
||||
- **Privacy**: _See [Uptime Kuma](https://github.com/louislam/uptime-kuma)_
|
||||
|
||||
---
|
||||
|
||||
## System Resource Monitoring
|
||||
|
||||
### Glances
|
||||
@ -2300,8 +2343,28 @@ Glances is a cross-platform monitoring tool developed by [@nicolargo](https://gi
|
||||
|
||||
If you don't already have it installed, either follow the [Installation Guide](https://github.com/nicolargo/glances/blob/master/README.rst) for your system, or setup [with Docker](https://glances.readthedocs.io/en/latest/docker.html), or use the one-line install script: `curl -L https://bit.ly/glances | /bin/bash`.
|
||||
|
||||
If you are using Docker to run glances make sure to add the enviroment variable `-e TZ = {YourTimeZone}`. You can get a list of valid timezones by running `timedatectl list-timezones` on any linux system. This is needed so the graphs show the currect time.
|
||||
|
||||
Here an example for Docker
|
||||
```
|
||||
docker run -d \
|
||||
--name glances \
|
||||
--restart unless-stopped \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
||||
-p 61208:61208 \
|
||||
--pid host \
|
||||
--privileged \
|
||||
-e GLANCES_OPT=-w \
|
||||
-e PUID=1000 \
|
||||
-e PGID=1000 \
|
||||
-e TZ=Europe/Zurich \
|
||||
nicolargo/glances:latest
|
||||
```
|
||||
|
||||
Glances can be launched with the `glances` command. You'll need to run it in web server mode, using the `-w` option for the API to be reachable. If you don't plan on using the Web UI, then you can disable it using `--disable-webui`. See the [command reference docs](https://glances.readthedocs.io/en/latest/cmds.html) for more info.
|
||||
|
||||
If Glaces is running on a Windows system it is recommanded to add the following arguments ```--disable-plugin all --enable-plugin cpu,mem,diskio,ip,network,containers,quicklook,load,fs,alert -w``` This is due to Glances not being that stable on windows, so disabling all plugins that aren't used by Dashy widgets can save on ressources.
|
||||
|
||||
#### Options
|
||||
|
||||
All Glance's based widgets require a `hostname`. All other parameters are optional.
|
||||
|
12
netlify.toml
12
netlify.toml
@ -8,14 +8,12 @@
|
||||
command = "yarn build"
|
||||
publish = "dist"
|
||||
functions = "services/serverless-functions"
|
||||
environment = { NODE_VERSION = "16.13.2" }
|
||||
|
||||
# Site info, used for the 1-Click deploy page
|
||||
[template.environment]
|
||||
STATUSKIT_PAGE_TITLE = "Dashy"
|
||||
STATUSKIT_COMPANY_LOGO = "https://raw.githubusercontent.com/Lissy93/dashy/master/docs/assets/logo.png"
|
||||
STATUSKIT_SUPPORT_CONTACT_LINK = "https://github.com/lissy93/dashy"
|
||||
STATUSKIT_RESOURCES_LINK = "https://dashy.to/docs"
|
||||
# Environmental variables for build command
|
||||
[build.environment]
|
||||
NODE_VERSION = "20.11.1"
|
||||
NODE_OPTIONS = "--openssl-legacy-provider"
|
||||
YARN_FLAGS = "--ignore-engines"
|
||||
|
||||
# Redirect the Node endpoints to serverless functions
|
||||
[[redirects]]
|
||||
|
29
package.json
29
package.json
@ -1,25 +1,26 @@
|
||||
{
|
||||
"name": "Dashy",
|
||||
"version": "2.2.0",
|
||||
"name": "dashy",
|
||||
"version": "3.0.0",
|
||||
"license": "MIT",
|
||||
"main": "server",
|
||||
"author": "Alicia Sykes <alicia@omg.lol> (https://aliciasykes.com)",
|
||||
"scripts": {
|
||||
"start": "node server",
|
||||
"dev": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"dev": "NODE_OPTIONS=--openssl-legacy-provider vue-cli-service serve",
|
||||
"build": "NODE_OPTIONS=--openssl-legacy-provider vue-cli-service build",
|
||||
"lint": "vue-cli-service lint",
|
||||
"pm2-start": "npx pm2 start server.js",
|
||||
"build-watch": "vue-cli-service build --watch --mode production",
|
||||
"build-and-start": "npm-run-all --parallel build-watch start",
|
||||
"build-and-start": "NODE_OPTIONS=--openssl-legacy-provider npm-run-all --parallel build start",
|
||||
"validate-config": "node services/config-validator",
|
||||
"health-check": "node services/healthcheck",
|
||||
"dependency-audit": "npx improved-yarn-audit --ignore-dev-deps"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.0.0",
|
||||
"@formschema/native": "^2.0.0-beta.6",
|
||||
"@sentry/tracing": "^6.17.5",
|
||||
"@sentry/vue": "^6.17.4",
|
||||
"@sentry/tracing": "^7.102.1",
|
||||
"@sentry/vue": "^7.102.1",
|
||||
"ajv": "^8.10.0",
|
||||
"axios": "^1.6.0",
|
||||
"connect-history-api-fallback": "^1.6.0",
|
||||
@ -30,6 +31,7 @@
|
||||
"keycloak-js": "^20.0.3",
|
||||
"register-service-worker": "^1.7.2",
|
||||
"remedial": "^1.0.8",
|
||||
"rss-parser": "3.13.0",
|
||||
"rsup-progress": "^3.2.0",
|
||||
"simple-icons": "^7.19.0",
|
||||
"v-jsoneditor": "^1.4.5",
|
||||
@ -43,31 +45,30 @@
|
||||
"vue-select": "^3.20.2",
|
||||
"vue-swatches": "^2.1.1",
|
||||
"vue-toasted": "^1.1.28",
|
||||
"vuex": "^3.6.2",
|
||||
"rss-parser": "3.13.0"
|
||||
"vuex": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@architect/sandbox": "^4.5.2",
|
||||
"@babel/preset-env": "^7.17.10",
|
||||
"@vue/cli-plugin-babel": "^4.5.15",
|
||||
"@vue/cli-plugin-eslint": "^4.5.15",
|
||||
"@vue/cli-plugin-pwa": "^4.5.15",
|
||||
"@vue/cli-service": "^4.5.15",
|
||||
"@vue/cli-plugin-typescript": "^5.0.8",
|
||||
"@vue/cli-service": "^4.5.19",
|
||||
"@vue/eslint-config-standard": "^4.0.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"eslint": "^7.24.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-airbnb": "^18.0.1",
|
||||
"eslint-plugin-vue": "^7.9.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"progress-bar-webpack-plugin": "^2.1.0",
|
||||
"sass": "^1.38.0",
|
||||
"sass-loader": "^7.1.0",
|
||||
"typescript": "^5.4.4",
|
||||
"vue-cli-plugin-yaml": "^1.0.2",
|
||||
"vue-svg-loader": "^0.16.0",
|
||||
"vue-template-compiler": "^2.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0 <18.0.0"
|
||||
"node": ">=16.0.0 <21.6.2"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<!-- Dashy: Licensed under MIT, (C) 2021 Alicia Sykes <https://aliciasykes.com> -->
|
||||
<!-- Dashy: Licensed under MIT, (C) 2024 Alicia Sykes <https://aliciasykes.com> -->
|
||||
<!-- This is the default page, displayed while the app is still building -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* Styles applied to index.html for the loading screen, prior to the app being injected */
|
||||
/* Dashy - Licensed under MIT, (C) Alicia Sykes 2022 */
|
||||
/* Dashy - Licensed under MIT, (C) Alicia Sykes 2024 */
|
||||
|
||||
body { margin: 0; }
|
||||
#app .loading-placeholder {
|
||||
|
28
server.js
28
server.js
@ -18,7 +18,9 @@ const history = require('connect-history-api-fallback');
|
||||
|
||||
/* Kick of some basic checks */
|
||||
require('./services/update-checker'); // Checks if there are any updates available, prints message
|
||||
require('./services/config-validator'); // Include and kicks off the config file validation script
|
||||
|
||||
let config = {}; // setup the config
|
||||
config = require('./services/config-validator'); // Include and kicks off the config file validation script
|
||||
|
||||
/* Include route handlers for API endpoints */
|
||||
const statusCheck = require('./services/status-check'); // Used by the status check feature, uses GET
|
||||
@ -27,6 +29,7 @@ const rebuild = require('./services/rebuild-app'); // A script to programmatical
|
||||
const systemInfo = require('./services/system-info'); // Basic system info, for resource widget
|
||||
const sslServer = require('./services/ssl-server'); // TLS-enabled web server
|
||||
const corsProxy = require('./services/cors-proxy'); // Enables API requests to CORS-blocked services
|
||||
const getUser = require('./services/get-user'); // Enables server side user lookup
|
||||
|
||||
/* Helper functions, and default config */
|
||||
const printMessage = require('./services/print-message'); // Function to print welcome msg on start
|
||||
@ -35,8 +38,8 @@ const ENDPOINTS = require('./src/utils/defaults').serviceEndpoints; // API endpo
|
||||
/* Checks if app is running within a container, from env var */
|
||||
const isDocker = !!process.env.IS_DOCKER;
|
||||
|
||||
/* Checks env var for port. If undefined, will use Port 80 for Docker, or 4000 for metal */
|
||||
const port = process.env.PORT || (isDocker ? 80 : 4000);
|
||||
/* Checks env var for port. If undefined, will use Port 8080 for Docker, or 4000 for metal */
|
||||
const port = process.env.PORT || (isDocker ? 8080 : 4000);
|
||||
|
||||
/* Checks env var for host. If undefined, will use 0.0.0.0 */
|
||||
const host = process.env.HOST || '0.0.0.0';
|
||||
@ -73,6 +76,7 @@ const app = express()
|
||||
.use(sslServer.middleware)
|
||||
// Serves up static files
|
||||
.use(express.static(path.join(__dirname, 'dist')))
|
||||
.use(express.static(path.join(__dirname, process.env.USER_DATA_DIR || 'user-data')))
|
||||
.use(express.static(path.join(__dirname, 'public'), { index: 'initialization.html' }))
|
||||
// Load middlewares for parsing JSON, and supporting HTML5 history routing
|
||||
.use(express.json({ limit: '1mb' }))
|
||||
@ -87,10 +91,11 @@ const app = express()
|
||||
printWarning(`Error running status check for ${req.url}\n`, e);
|
||||
}
|
||||
})
|
||||
// POST Endpoint used to save config, by writing conf.yml to disk
|
||||
// POST Endpoint used to save config, by writing config file to disk
|
||||
.use(ENDPOINTS.save, method('POST', (req, res) => {
|
||||
try {
|
||||
saveConfig(req.body, (results) => { res.end(results); });
|
||||
config = req.body.config; // update the config
|
||||
} catch (e) {
|
||||
printWarning('Error writing config file to disk', e);
|
||||
res.end(JSON.stringify({ success: false, message: e }));
|
||||
@ -122,8 +127,19 @@ const app = express()
|
||||
res.end(JSON.stringify({ success: false, message: e }));
|
||||
}
|
||||
})
|
||||
// GET fallback endpoint
|
||||
.get('*', (req, res) => res.sendFile(path.join(__dirname, 'dist', 'index.html')));
|
||||
// GET endpoint to return user info
|
||||
.use(ENDPOINTS.getUser, (req, res) => {
|
||||
try {
|
||||
const user = getUser(config, req);
|
||||
res.end(JSON.stringify(user));
|
||||
} catch (e) {
|
||||
res.end(JSON.stringify({ success: false, message: e }));
|
||||
}
|
||||
})
|
||||
// If no other route is matched, serve up the index.html with a 404 status
|
||||
.use((req, res) => {
|
||||
res.status(404).sendFile(path.join(__dirname, 'dist', 'index.html'));
|
||||
});
|
||||
|
||||
/* Create HTTP server from app on port, and print welcome message */
|
||||
http.createServer(app)
|
||||
|
@ -98,11 +98,14 @@ const printFileReadError = (e) => {
|
||||
}
|
||||
};
|
||||
|
||||
let config = {};
|
||||
|
||||
try { // Try to open and parse the YAML file
|
||||
const config = yaml.load(fs.readFileSync('./public/conf.yml', 'utf8'));
|
||||
config = yaml.load(fs.readFileSync(`./${process.env.USER_DATA_DIR || 'user-data'}/conf.yml`, 'utf8'));
|
||||
validate(config);
|
||||
} catch (e) { // Something went very wrong...
|
||||
setIsValidVariable(false);
|
||||
logToConsole(bigError());
|
||||
printFileReadError(e);
|
||||
}
|
||||
module.exports = config;
|
||||
|
15
services/get-user.js
Normal file
15
services/get-user.js
Normal file
@ -0,0 +1,15 @@
|
||||
module.exports = (config, req) => {
|
||||
try {
|
||||
if ( config.appConfig.auth.enableHeaderAuth ) {
|
||||
const userHeader = config.appConfig.auth.headerAuth.userHeader;
|
||||
const proxyWhitelist = config.appConfig.auth.headerAuth.proxyWhitelist;
|
||||
if ( proxyWhitelist.includes(req.socket.remoteAddress) ) {
|
||||
return { "success": true, "user": req.headers[userHeader.toLowerCase()] };
|
||||
}
|
||||
}
|
||||
return {};
|
||||
} catch (e) {
|
||||
console.warn("Error get-user: ", e);
|
||||
return { 'success': false };
|
||||
}
|
||||
};
|
@ -6,11 +6,17 @@
|
||||
|
||||
const isSsl = !!process.env.SSL_PRIV_KEY_PATH && !!process.env.SSL_PUB_KEY_PATH;
|
||||
|
||||
// eslint-disable-next-line import/no-dynamic-require
|
||||
const http = require(isSsl ? 'https' : 'http');
|
||||
|
||||
/* Location of the server to test */
|
||||
const isDocker = !!process.env.IS_DOCKER;
|
||||
const port = isSsl ? (process.env.SSL_PORT || (isDocker ? 443 : 4001)) : (process.env.PORT || (isDocker ? 80 : 4000));
|
||||
|
||||
/* Get the port to use (depending on, if docker, if SSL) */
|
||||
const sslPort = process.env.SSL_PORT || (isDocker ? 443 : 4001);
|
||||
const normalPort = process.env.PORT || (isDocker ? 8080 : 4000);
|
||||
const port = isSsl ? sslPort : normalPort;
|
||||
|
||||
const host = process.env.HOST || '0.0.0.0';
|
||||
const timeout = 2000;
|
||||
|
||||
@ -18,7 +24,9 @@ const agent = new http.Agent({
|
||||
rejectUnauthorized: false, // Allow self-signed certificates
|
||||
});
|
||||
|
||||
const requestOptions = { host, port, timeout, agent };
|
||||
const requestOptions = {
|
||||
host, port, timeout, agent,
|
||||
};
|
||||
|
||||
const startTime = new Date(); // Initialize timestamp to calculate time taken
|
||||
|
||||
|
@ -32,7 +32,7 @@ module.exports = (ip, port, isDocker) => {
|
||||
} else {
|
||||
// Prepare message for users running app on bare metal
|
||||
msg = `${chars.GREEN}┏${line(75)}┓${chars.BR}`
|
||||
+ `┃ ${chars.CYAN}Welcome to Dashy! 🚀${blanks(55)}${chars.GREEN}┃${chars.BR}`
|
||||
+ `┃ ${chars.CYAN}Welcome to Dashy! 🚀${blanks(54)}${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}`
|
||||
+ `┗${line(75)}┛${chars.BR}${chars.BR}${chars.RESET}`;
|
||||
|
@ -18,15 +18,15 @@ module.exports = async (newConfig, render) => {
|
||||
|
||||
// Define constants for the config file
|
||||
const settings = {
|
||||
defaultLocation: './public/',
|
||||
defaultLocation: process.env.USER_DATA_DIR || './user-data/',
|
||||
defaultFile: 'conf.yml',
|
||||
filename: 'conf',
|
||||
backupDenominator: '.backup.yml',
|
||||
};
|
||||
|
||||
// Make the full file name and path to save the backup config file
|
||||
const backupFilePath = path.normalize(process.env.BACKUP_DIR || settings.defaultLocation)
|
||||
+ `/${usersFileName || settings.filename}-`
|
||||
const backupFilePath = `${path.normalize(process.env.BACKUP_DIR || settings.defaultLocation)
|
||||
}/${usersFileName || settings.filename}-`
|
||||
+ `${Math.round(new Date() / 1000)}${settings.backupDenominator}`;
|
||||
|
||||
// The path where the main conf.yml should be read and saved to
|
||||
@ -48,12 +48,12 @@ module.exports = async (newConfig, render) => {
|
||||
// Makes a backup of the existing config file
|
||||
await fsPromises
|
||||
.copyFile(defaultFilePath, backupFilePath)
|
||||
.catch((error) => render(getRenderMessage(false, `Unable to backup conf.yml: ${error}`)));
|
||||
.catch((error) => render(getRenderMessage(false, `Unable to backup ${settings.defaultFile}: ${error}`)));
|
||||
|
||||
// Writes the new content to the conf.yml file
|
||||
await fsPromises
|
||||
.writeFile(defaultFilePath, newConfig.config.toString(), writeFileOptions)
|
||||
.catch((error) => render(getRenderMessage(false, `Unable to write to conf.yml: ${error}`)));
|
||||
.catch((error) => render(getRenderMessage(false, `Unable to write to ${settings.defaultFile}: ${error}`)));
|
||||
|
||||
// If successful, then render hasn't yet been called- call it
|
||||
await render(getRenderMessage(true));
|
||||
|
@ -64,7 +64,7 @@ export default {
|
||||
return this.$store.getters.pageInfo;
|
||||
},
|
||||
sections() {
|
||||
return this.$store.getters.pageInfo;
|
||||
return this.$store.getters.sections;
|
||||
},
|
||||
visibleComponents() {
|
||||
return this.$store.getters.visibleComponents;
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"home": {
|
||||
"no-results": "لا نتائج للبحث",
|
||||
"no-data": "لم يتم تكوين بيانات"
|
||||
"no-data": "لا يوجد بيانات"
|
||||
},
|
||||
"search": {
|
||||
"search-label": "بحث",
|
||||
@ -33,11 +33,11 @@
|
||||
},
|
||||
"config": {
|
||||
"main-tab": "القائمة الرئيسية",
|
||||
"view-config-tab": "مشاهدة ملف Config",
|
||||
"edit-config-tab": "تحرير التكوين",
|
||||
"view-config-tab": "مشاهده ملف الإعدادات",
|
||||
"edit-config-tab": "تغير ملف الاع",
|
||||
"custom-css-tab": "الأنماط المخصصة",
|
||||
"heading": "خيارات الإعداد",
|
||||
"download-config-button": "تنزيل Config",
|
||||
"download-config-button": "تنزيل ملف الإعدادات",
|
||||
"edit-config-button": "تحرير التكوين",
|
||||
"edit-css-button": "تحرير CSS مخصص",
|
||||
"cloud-sync-button": "قم بتمكين Cloud Sync",
|
||||
@ -84,7 +84,7 @@
|
||||
"sign-in-welcome": "مرحبًا {username}!"
|
||||
},
|
||||
"updates": {
|
||||
"app-version-note": "نسخة متهورة",
|
||||
"app-version-note": "ملاحظة نسخة التطبيق",
|
||||
"up-to-date": "حتى الآن",
|
||||
"out-of-date": "التحديث متاح",
|
||||
"unsupported-version-l1": "أنت تستخدم إصدارًا غير مدعوم من Dashy",
|
||||
|
@ -1,421 +1,448 @@
|
||||
{
|
||||
"home":
|
||||
{
|
||||
"no-results": "keine Suchergebnisse",
|
||||
"no-data": "keine Daten konfiguriert",
|
||||
"no-items-section": "Noch keine Elemente zum Anzeigen"
|
||||
},
|
||||
"search":
|
||||
{
|
||||
"search-label": "Suche",
|
||||
"search-placeholder": "Tippen um zu filtern",
|
||||
"clear-search-tooltip": "Suchfeld leeren",
|
||||
"enter-to-search-web": "Drücke Enter um das Internet zu durchsuchen"
|
||||
},
|
||||
"login":
|
||||
{
|
||||
"title": "Dashy",
|
||||
"username-label": "Benutzername",
|
||||
"password-label": "Passwort",
|
||||
"login-button": "Anmelden",
|
||||
"remember-me-label": "Angemeldet bleiben für",
|
||||
"remember-me-never": "Niemals",
|
||||
"remember-me-hour": "4 Stunden",
|
||||
"remember-me-day": "1 Tag",
|
||||
"remember-me-week": "1 Woche",
|
||||
"remember-me-long-time": "Eine lange Zeit",
|
||||
"error-missing-username": "Benutzername fehlt",
|
||||
"error-missing-password": "Passwort fehlt",
|
||||
"error-incorrect-username": "Benutzer nicht gefunden",
|
||||
"error-incorrect-password": "Falsches Passwort",
|
||||
"success-message": "Anmeldung läuft...",
|
||||
"logout-message": "Abgemeldet",
|
||||
"already-logged-in-title": "Bereits angemeldet",
|
||||
"already-logged-in-text": "Angemeldet als",
|
||||
"proceed-to-dashboard": "Zum Dashboard fortfahren",
|
||||
"log-out-button": "Abmelden",
|
||||
"proceed-guest-button": "Als Gast fortfahren"
|
||||
},
|
||||
"config":
|
||||
{
|
||||
"main-tab": "Hauptmenü",
|
||||
"view-config-tab": "Konfiguration",
|
||||
"edit-config-tab": "Konfiguration bearbeiten",
|
||||
"custom-css-tab": "eigene CSS",
|
||||
"heading": "Konfigurationseinstellungen",
|
||||
"download-config-button": "Konfigurationsdownload",
|
||||
"edit-config-button": "Konfiguration bearbeiten",
|
||||
"edit-css-button": "CSS bearbeiten",
|
||||
"cloud-sync-button": "Cloud-Synchronisation aktivieren",
|
||||
"edit-cloud-sync-button": "Cloud-Synchronisation bearbeiten",
|
||||
"rebuild-app-button": "Anwendung neu kompilieren",
|
||||
"change-language-button": "App-Sprache ändern",
|
||||
"reset-settings-button": "lokale Einstellungen zurücksetzen",
|
||||
"app-info-button": "App Informationen",
|
||||
"backup-note": "Es wird empfohlen ein Backup der Konfiguration zu erstellen bevor Änderungen durchgeführt werden.",
|
||||
"reset-config-msg-l1": "Dadurch werden alle Benutzereinstellungen aus dem lokalen Speicher entfernt, dies hat jedoch keine Auswirkungen auf Ihre Datei 'conf.yml'.",
|
||||
"reset-config-msg-l2": "Sie sollten zuerst alle Änderungen, die Sie lokal vorgenommen haben, sichern, wenn Sie sie in Zukunft wiederverwenden möchten.",
|
||||
"reset-config-msg-l3": "Sind Sie sicher, dass Sie fortfahren möchten?",
|
||||
"data-cleared-msg": "Daten erfolgreich gelöscht",
|
||||
"actions-label": "Aktionen",
|
||||
"copy-config-label": "Konfiguration kopieren",
|
||||
"data-copied-msg": "Konfiguration wurde in die Zwischenablage kopiert",
|
||||
"reset-config-label": "Konfiguration zurücksetzen",
|
||||
"css-save-btn": "Änderungen speichern",
|
||||
"css-note-label": "Bemerkung",
|
||||
"css-note-l1": "Sie müssen die Seite aktualisieren, damit Ihre Änderungen wirksam werden.",
|
||||
"css-note-l2": "Stilüberschreibungen werden nur lokal gespeichert, daher wird empfohlen vorher eine Kopie Ihres CSS zu erstellen.",
|
||||
"css-note-l3": "Um alle benutzerdefinierten Stile zu entfernen löschen Sie den Inhalt und klicken Sie auf Änderungen speichern."
|
||||
},
|
||||
"alternate-views": {
|
||||
"alternate-view-heading": "Ansicht wechseln",
|
||||
"default": "Standard",
|
||||
"workspace": "Arbeitsplatz",
|
||||
"minimal": "Minimal"
|
||||
},
|
||||
"settings":
|
||||
{
|
||||
"theme-label": "Design",
|
||||
"layout-label": "Layout",
|
||||
"layout-auto": "Auto",
|
||||
"layout-horizontal": "Horizontal",
|
||||
"layout-vertical": "Vertikal",
|
||||
"item-size-label": "Itemgröße",
|
||||
"item-size-small": "klein",
|
||||
"item-size-medium": "mittel",
|
||||
"item-size-large": "groß",
|
||||
"config-launcher-label": "Konfiguration",
|
||||
"config-launcher-tooltip": "Konfiguration aktualisieren",
|
||||
"sign-out-tooltip": "Abmelden",
|
||||
"sign-in-tooltip": "Anmelden",
|
||||
"sign-in-welcome": "Hallo {username}!"
|
||||
},
|
||||
"updates":
|
||||
{
|
||||
"app-version-note": "Dashy Version",
|
||||
"up-to-date": "Aktuell",
|
||||
"out-of-date": "Update verfügbar",
|
||||
"unsupported-version-l1": "Sie verwenden eine nicht unterstützte Version von Dashy",
|
||||
"unsupported-version-l2": "Für die beste Erfahrung und aktuelle Sicherheitspatches aktualisieren Sie bitte auf"
|
||||
},
|
||||
"language-switcher":
|
||||
{
|
||||
"title": "Applikationssprache ändern",
|
||||
"dropdown-label": "Sprache auswählen",
|
||||
"save-button": "Speichern",
|
||||
"success-msg": "Sprache geändert auf"
|
||||
},
|
||||
"theme-maker":
|
||||
{
|
||||
"title": "Design Konfigurator",
|
||||
"export-button": "Benutzerdefinierte Variablen exportieren",
|
||||
"reset-button": "CSS zurücksetzen für",
|
||||
"show-all-button": "Alle Variablen anzeigen",
|
||||
"change-fonts-button": "Schriftart ändern",
|
||||
"save-button": "Speichern",
|
||||
"cancel-button": "Abbrechen",
|
||||
"saved-toast": "{theme} wurde erfolgreich aktualisiert",
|
||||
"copied-toast": "Designdaten für {theme} wurden in die Zwischenablage kopiert.",
|
||||
"reset-toast": "Benutzerdefinierte Farben für {theme} wurden entfernt"
|
||||
},
|
||||
"config-editor":
|
||||
{
|
||||
"save-location-label": "Speicherort",
|
||||
"location-local-label": "Lokal anwenden",
|
||||
"location-disk-label": "Änderungen in die Konfigurationsdatei schreiben",
|
||||
"save-button": "Änderungen speichern",
|
||||
"preview-button": "Vorschau der Änderungen",
|
||||
"valid-label": "Syntax ist gültig",
|
||||
"status-success-msg": "Aufgabe abgeschlossen",
|
||||
"status-fail-msg": "Aufgabe fehlgeschlagen",
|
||||
"success-msg-disk": "Konfigurationsdatei wurde erfolgreich auf die Festplatte geschrieben",
|
||||
"success-msg-local": "Lokale Änderungen wurden erfolgreich gespeichert",
|
||||
"success-note-l1": "Die Applikation sollte automatisch re-kompiliert werden.",
|
||||
"success-note-l2": "Dies kann bis zu einer Minute dauern.",
|
||||
"success-note-l3": "Sie müssen die Seite aktualisieren damit die Änderungen wirksam werden.",
|
||||
"error-msg-save-mode": "Bitte wählen Sie einen Speichermodus: Lokal oder Datei",
|
||||
"error-msg-cannot-save": "Beim Speichern der Konfiguration ist ein Fehler aufgetreten",
|
||||
"error-msg-bad-json": "Fehler in JSON-Daten, möglicherweise fehlerhafter Syntax",
|
||||
"warning-msg-validation": "Validierungswarnung",
|
||||
"not-admin-note": "Änderungen können nicht auf die Festplatte gespeichert werden, da Sie nicht als Administrator angemeldet sind"
|
||||
},
|
||||
"app-rebuild":
|
||||
{
|
||||
"title": "Applikation re-kompilieren",
|
||||
"rebuild-note-l1": "Damit die in die Datei conf.yml geschriebenen Änderungen wirksam werden ist ein re-kompilieren erforderlich.",
|
||||
"rebuild-note-l2": "Dies sollte automatisch passieren, aber falls nicht können Sie es hier manuell starten.",
|
||||
"rebuild-note-l3": "Dies ist bei lokal gespeicherten Änderungen nicht erforderlich.",
|
||||
"rebuild-button": "Start Kompilierung",
|
||||
"rebuilding-status-1": "Baue...",
|
||||
"rebuilding-status-2": "Das kann ein paar Minuten dauern",
|
||||
"error-permission": "Sie sind nicht berechtigt diese Aktion auszulösen",
|
||||
"success-msg": "Kompilierung erfolgreich abgeschlossen",
|
||||
"fail-msg": "Kompilierung fehlgeschlagen",
|
||||
"reload-note": "Ein neu Laden der Seite ist erforderlich, damit die Änderungen wirksam werden.",
|
||||
"reload-button": "Seite neu laden"
|
||||
},
|
||||
"cloud-sync":
|
||||
{
|
||||
"title": "Cloud Backup & Wiederherstellung",
|
||||
"intro-l1": "Cloud-Backup und Wiederherstellung ist eine optionale Funktion mit der Sie Ihre Konfiguration in das Internet hochladen und dann auf einem anderen Gerät oder einer anderen Dashy-Instanz wiederherstellen können.",
|
||||
"intro-l2": "Alle Daten sind vollständig Ende-zu-Ende mit AES verschlüsselt. Ihr Passwort wird als Schlüssel verwendet.",
|
||||
"intro-l3": "Weitere Informationen finden Sie im",
|
||||
"backup-title-setup": "Backup erstellen",
|
||||
"backup-title-update": "Backup aktualisieren",
|
||||
"password-label-setup": "Passwort auswählen",
|
||||
"password-label-update": "Passwort eingeben",
|
||||
"backup-button-setup": "Backup",
|
||||
"backup-button-update": "Backup aktualisieren",
|
||||
"backup-id-label": "Ihre Backup ID",
|
||||
"backup-id-note": "Diese wird zusammen mit dem Passwort benötigt um Ihr Backup wiederherzustellen. Bewahren Sie sie zusammen mit Ihrem Passwort an einem sicheren Ort auf.",
|
||||
"restore-title": "Backup wiederherstellen",
|
||||
"restore-id-label": "ID wiederherstellen",
|
||||
"restore-password-label": "Passwort",
|
||||
"restore-button": "Wiederherstellen",
|
||||
"backup-error-unknown": "Anfrage kann nicht verarbeitet werden",
|
||||
"backup-error-password": "Falsches Passwort. Bitte geben Sie Ihr aktuelles Passwort ein.",
|
||||
"backup-success-msg": "Erfolgreich abgeschlossen",
|
||||
"restore-success-msg": "Konfiguration erfolgreich wiederhergestellt"
|
||||
},
|
||||
"menu":
|
||||
{
|
||||
"open-section-title": "Öffnen in",
|
||||
"sametab": "Aktueller Tab",
|
||||
"newtab": "Neuer Tab",
|
||||
"modal": "Popup Modal",
|
||||
"workspace": "Arbeitsflächenansicht",
|
||||
"options-section-title": "Optionen",
|
||||
"edit-item": "Bearbeiten",
|
||||
"move-item": "Kopieren oder Verschieben",
|
||||
"remove-item": "Entfernen"
|
||||
},
|
||||
"context-menus":
|
||||
{
|
||||
"item":
|
||||
{
|
||||
"open-section-title": "Öffnen in",
|
||||
"sametab": "Aktueller Tab",
|
||||
"newtab": "Neuer Tab",
|
||||
"modal": "Popup Modal",
|
||||
"workspace": "Arbeitsflächenansicht",
|
||||
"clipboard": "In Zwischenablage kopieren",
|
||||
"options-section-title": "Optionen",
|
||||
"edit-item": "Bearbeiten",
|
||||
"move-item": "Kopieren oder Verschieben",
|
||||
"remove-item": "Entfernen",
|
||||
"copied-toast": "URL wurde in die Zwischenablage kopiert"
|
||||
},
|
||||
"section":
|
||||
{
|
||||
"open-section": "Sektion öffnen",
|
||||
"edit-section": "Bearbeiten",
|
||||
"expand-collapse": "Aus- / Einklappen",
|
||||
"move-section": "Verschieben nach",
|
||||
"remove-section": "Entfernen"
|
||||
}
|
||||
},
|
||||
"interactive-editor":
|
||||
{
|
||||
"menu":
|
||||
{
|
||||
"start-editing-tooltip": "Interaktiven Editor starten",
|
||||
"edit-site-data-subheading": "Seiteninformationen bearbeiten",
|
||||
"edit-page-info-btn": "Seiteninformationen bearbeiten",
|
||||
"edit-page-info-tooltip": "Applikationstitel, Beschreibung, Nav. links, Fußzeile, etc.",
|
||||
"edit-app-config-btn": "Applikationskonfiguration bearbeiten",
|
||||
"edit-app-config-tooltip": "Alle anderen Konfigurationsoptionen",
|
||||
"edit-pages-btn": "Seiten bearbeiten",
|
||||
"edit-pages-tooltip": "Hinzufügen oder entfernen von zusätzlichen Ansichten",
|
||||
"config-save-methods-subheading": "Speicheroptionen der Konfiguration",
|
||||
"save-locally-btn": "Lokal speichern",
|
||||
"save-locally-tooltip": "Konfiguration lokal im Browser speichern. Dies hat keinen Einfluss auf die Konfigurationsdatei, aber Änderungen werden nur in diesem Browser gespeichert",
|
||||
"save-disk-btn": "Auf Festplatte speichern",
|
||||
"save-disk-tooltip": "Konfigurationsdatei conf.yml speichern. Dies erzeugt ein Backup und überschreibt dann die existierende Konfigurationsdatei",
|
||||
"export-config-btn": "Konfiguration exportieren",
|
||||
"export-config-tooltip": "Konfiguration anzeigen und exportieren, entweder in eine Datei oder in die Zwischenablage",
|
||||
"cloud-backup-btn": "Cloud-Backup starten",
|
||||
"cloud-backup-tooltip": "Speichert ein verrschlüsseltes Backup in die Cloud",
|
||||
"edit-raw-config-btn": "Konfiguration als Rohdaten bearbeiten",
|
||||
"edit-raw-config-tooltip": "Anzeigen und bearbeiten der Konfiguration als Rohdaten im JSON-Editor",
|
||||
"cancel-changes-btn": "Verwerfen",
|
||||
"cancel-changes-tooltip": "Modifikationen zurücksetzen und Bearbeitungsmodus schließen. Dies hat keinen Einfluss auf die Konfigurationsdatei",
|
||||
"edit-mode-name": "Bearbeitung",
|
||||
"edit-mode-subtitle": "Sie sind im Bearbeitungsmodus",
|
||||
"edit-mode-description": "Das bedeutet, dass Änderungen an der Konfigurationsdatei vorgenommen werden können. Änderungen können vor dem Speichern betrachtet werden.",
|
||||
"save-stage-btn": "Speichern",
|
||||
"cancel-stage-btn": "Abbrechen",
|
||||
"save-locally-warning": "Wenn Sie fortfahren werden die Änderungen nur in Ihrem Browser gespeichert. Um die Konfiguration auf anderen Geräten zu nutzen sollten Sie sie exportieren. Möchten Sie fortfahren?"
|
||||
},
|
||||
"edit-item":
|
||||
{
|
||||
"missing-title-err": "Ein Titel is zwingend notwendig"
|
||||
},
|
||||
"edit-section":
|
||||
{
|
||||
"edit-section-title": "Sektion bearbeiten",
|
||||
"add-section-title": "Neue Sektion hinzufügen",
|
||||
"edit-tooltip": "Klicken zum Bearbeiten oder Rechtsklick für weitere Optionen",
|
||||
"remove-confirm": "Sind Sie sicher, dass sie diese Sektion entfernen möchten? Diese Aktion kann nicht rückgänging gemacht werden."
|
||||
},
|
||||
"edit-app-config":
|
||||
{
|
||||
"warning-msg-title": "Ab hier ist Vorsicht geboten",
|
||||
"warning-msg-l1": "Die folgenden Optionen sind für fortgeschrittene Konfigurationen.",
|
||||
"warning-msg-l2": "Sollten Felder unklar sein, konsultieren Sie die",
|
||||
"warning-msg-docs": "Dokumentation",
|
||||
"warning-msg-l3": "um unbeabsichtigte Folgen zu vermeiden."
|
||||
},
|
||||
"export":
|
||||
{
|
||||
"export-title": "Konfiguration exportieren",
|
||||
"copy-clipboard-btn": "In Zwischenablage kopieren",
|
||||
"copy-clipboard-tooltip": "Applikationskonfiguration als YAML in Zwischenablage kopieren",
|
||||
"download-file-btn": "Datei herunterladen",
|
||||
"download-file-tooltip": "Applikationskonfiguration auf Ihr Gerät herunterladen",
|
||||
"view-title": "Konfiguration anzeigen"
|
||||
}
|
||||
},
|
||||
"widgets":
|
||||
{
|
||||
"general":
|
||||
{
|
||||
"loading": "Lade...",
|
||||
"show-more": "Details",
|
||||
"show-less": "Weniger anzeigen",
|
||||
"open-link": "Weiterlesen"
|
||||
},
|
||||
"pi-hole":
|
||||
{
|
||||
"status-heading": "Status"
|
||||
},
|
||||
"stat-ping":
|
||||
{
|
||||
"up": "Online",
|
||||
"down": "Offline"
|
||||
},
|
||||
"net-data":
|
||||
{
|
||||
"cpu-chart-title": "CPU Historie",
|
||||
"mem-chart-title": "Speichernutzung",
|
||||
"mem-breakdown-title": "Speichernutzung",
|
||||
"load-chart-title": "Systemlast"
|
||||
},
|
||||
"glances":
|
||||
{
|
||||
"disk-space-free": "Frei",
|
||||
"disk-space-used": "Genutzt",
|
||||
"disk-mount-point": "Mount Punkt",
|
||||
"disk-file-system": "Dateisystem,",
|
||||
"disk-io-read": "Lesen",
|
||||
"disk-io-write": "Schreiben",
|
||||
"system-load-desc": "Prozesse in Warteschlange. (Durchschnitt aller Kerne)"
|
||||
},
|
||||
"system-info":
|
||||
{
|
||||
"uptime": "Uptime"
|
||||
},
|
||||
"flight-data":
|
||||
{
|
||||
"arrivals": "Ankünfte",
|
||||
"departures": "Abflüge"
|
||||
},
|
||||
"tfl-status":
|
||||
{
|
||||
"good-service-all": "Guter Service auf allen Leitungen",
|
||||
"good-service-rest": "Guter Service auf allen anderen Leitungen"
|
||||
},
|
||||
"synology-download":
|
||||
{
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"downloaded": "Heruntergeladen",
|
||||
"uploaded": "Hochgeladen",
|
||||
"remaining": "Verbleibend",
|
||||
"up": "Hoch",
|
||||
"down": "Runter"
|
||||
},
|
||||
"gluetun-status":
|
||||
{
|
||||
"vpn-ip": "VPN IP",
|
||||
"country": "Land",
|
||||
"region": "Bundesland",
|
||||
"city": "Stadt",
|
||||
"post-code": "Postleitzahl",
|
||||
"location": "Standort",
|
||||
"timezone": "Zeitzone",
|
||||
"organization": "Organisation"
|
||||
},
|
||||
"nextcloud":
|
||||
{
|
||||
"active": "Aktiv",
|
||||
"and": "und",
|
||||
"applications": "Anwendungen",
|
||||
"available": "Verfügbar",
|
||||
"away": "Abwesend",
|
||||
"cache-full": "CACHE VOLL",
|
||||
"chat-room": "Chatraum",
|
||||
"delete-all": "Alle löschen",
|
||||
"delete-notification": "Benachrichtigung löschen",
|
||||
"disabled": "Deaktivert",
|
||||
"disk-quota": "Disk Quota",
|
||||
"disk-space": "Disk Speicherplatz",
|
||||
"dnd": "Nicht stören",
|
||||
"email": "EMail",
|
||||
"enabled": "Aktiviert",
|
||||
"federated-shares-ucfirst": "Verbundsfreigaben",
|
||||
"federated-shares": "Verbundsfreigaben",
|
||||
"files": "Dateien{plural}",
|
||||
"free": "Frei",
|
||||
"groups": "Gruppen",
|
||||
"hit-rate": "Trefferrate",
|
||||
"hits": "Treffer",
|
||||
"home": "Zuhause",
|
||||
"in": "in",
|
||||
"keys": "Schlüssel",
|
||||
"last-24-hours": "Letzte 24 Stunden",
|
||||
"last-5-minutes": "in den letzten 5 Minuten",
|
||||
"last-hour": "in der letzten Stunde",
|
||||
"last-login": "Letzte Anmeldung",
|
||||
"last-restart": "Letzter Neustart",
|
||||
"load-averages": "Systemlast aller CPU-Kerne",
|
||||
"local-shares": "Lokale Freigaben",
|
||||
"local": "Lokal",
|
||||
"max-keys": "Maximale Schlüssel",
|
||||
"memory-used": "Speuchernutzung",
|
||||
"memory-utilisation": "Speuchernutzung",
|
||||
"memory": "Speicher",
|
||||
"misses": "Fehlschläge",
|
||||
"no-notifications": "Keine Benachrichtigungen",
|
||||
"no-pending-updates": "Keine ausstehenden Aktualisierungen",
|
||||
"nothing-to-show": "Momentan gibt es hier nichts zu zeigen",
|
||||
"of-which": "welche",
|
||||
"of": "von",
|
||||
"offline": "Offline",
|
||||
"online": "Online",
|
||||
"other": "Andere",
|
||||
"overall": "Insgesamt",
|
||||
"private-link": "privater Link",
|
||||
"public-link": "öffentlicher Link",
|
||||
"quota-enabled": "Disk Quota ist {nicht}aktiviert für diesen Benutzer",
|
||||
"received": "Empfangen",
|
||||
"scripts": "Skripte",
|
||||
"sent": "Gesendet",
|
||||
"started": "Gestartet",
|
||||
"storages-by-type": "Speicher nach Typ",
|
||||
"storages": "Speicher{plural}",
|
||||
"strings-use": "Strings benutzen",
|
||||
"tasks": "Aufgaben",
|
||||
"total-files": "Dateien gesamt",
|
||||
"total-users": "Benutzer gesamt",
|
||||
"total": "Insgesamt",
|
||||
"until": "Bis",
|
||||
"updates-available-for": "Aktualisierungen sind verfügbar für",
|
||||
"updates-available": "Aktualisierungen{plural} verfügbar",
|
||||
"used": "benutzt",
|
||||
"user": "Benutzer",
|
||||
"using": "nutzt",
|
||||
"version": "Version",
|
||||
"wasted": "verschwendet"
|
||||
}
|
||||
"home": {
|
||||
"no-results": "keine Suchergebnisse",
|
||||
"no-data": "keine Daten konfiguriert",
|
||||
"no-items-section": "Noch keine Elemente zum Anzeigen"
|
||||
},
|
||||
"search": {
|
||||
"search-label": "Suche",
|
||||
"search-placeholder": "Tippe um zu filtern",
|
||||
"clear-search-tooltip": "Suchfeld leeren",
|
||||
"enter-to-search-web": "Drücke Enter um das Internet zu durchsuchen"
|
||||
},
|
||||
"splash-screen": {
|
||||
"loading": "Lädt"
|
||||
},
|
||||
"login": {
|
||||
"title": "Dashy",
|
||||
"guest-label": "Gastzugriff",
|
||||
"username-label": "Benutzername",
|
||||
"password-label": "Passwort",
|
||||
"login-button": "Anmelden",
|
||||
"remember-me-label": "Angemeldet bleiben für",
|
||||
"remember-me-never": "Niemals",
|
||||
"remember-me-hour": "4 Stunden",
|
||||
"remember-me-day": "1 Tag",
|
||||
"remember-me-week": "1 Woche",
|
||||
"remember-me-long-time": "Eine lange Zeit",
|
||||
"error-missing-username": "Benutzername fehlt",
|
||||
"error-missing-password": "Passwort fehlt",
|
||||
"error-incorrect-username": "Benutzer nicht gefunden",
|
||||
"error-incorrect-password": "Falsches Passwort",
|
||||
"success-message": "Anmeldung läuft...",
|
||||
"logout-message": "Abgemeldet",
|
||||
"already-logged-in-title": "Bereits angemeldet",
|
||||
"already-logged-in-text": "Angemeldet als",
|
||||
"proceed-to-dashboard": "Zum Dashboard fortfahren",
|
||||
"log-out-button": "Abmelden",
|
||||
"proceed-guest-button": "Als Gast fortfahren",
|
||||
"guest-intro-1": "Diese Instanz hat Gastzugriffe aktiviert",
|
||||
"guest-intro-2": "Gäste haben lesenden Zugriff auf Dashboards, können also keine Veränderungen auf die Festplatte schreiben.",
|
||||
"error": "Error",
|
||||
"error-no-user-configured": "Authentifizierung ist nicht aktiviert, oder es sind keine Benutzer konfiguriert",
|
||||
"error-go-home-button": "Geh nach Hause",
|
||||
"logged-in-guest": "Als Gast eingeloggt, Umleitung...",
|
||||
"error-guest-access": "Gastzugriff nicht erlaubt"
|
||||
},
|
||||
"app-info": {
|
||||
"title": "App Info",
|
||||
"error-log": "Fehlerprotokoll",
|
||||
"no-errors": "Keine kürzlichen Fehler erkannt",
|
||||
"help-support": "Hilfe & Support",
|
||||
"help-support-description": "Um beim Betrieb oder der Konfiguration von Dashy Hilfe zu bekommen, siehe die",
|
||||
"help-support-discussions": "Diskussionen",
|
||||
"support-dashy": "Dashy unterstützen",
|
||||
"support-dashy-description": "Für Wege, sich zu beteiligen, besuchen Sie die",
|
||||
"support-dashy-link": "Contributions Seite",
|
||||
"report-bug": "Melde einen Fehler",
|
||||
"report-bug-description": "Wenn Sie glauben, einen Fehler gefunden zu haben, dann bitte",
|
||||
"report-bug-link": "öffne ein Issue",
|
||||
"more-info": "Mehr Informationen",
|
||||
"source": "Quellcode",
|
||||
"documentation": "Dokumentation",
|
||||
"privacy-and-security": "Datenschutz & Sicherheit",
|
||||
"privacy-and-security-l1": "Für eine Übersicht, wie Dashy mit Ihren Daten umgeht, siehe die",
|
||||
"privacy-and-security-privacy-policy": "Datenschutzerklärung",
|
||||
"privacy-and-security-advice": "Für Anleitung, um Ihr Dashboard zu sichern, konsultieren Sie die",
|
||||
"privacy-and-security-advice-link": "Management Dokumentation",
|
||||
"privacy-and-security-security-issue": "Wenn Sie glauben, eine mögliche Sicherheitslücke gefunden zu haben, melde sie wie beschrieben in unserer",
|
||||
"privacy-and-security-security-policy": "Security Policy",
|
||||
"license": "Lizenz",
|
||||
"license-under": "Lizensiert unter",
|
||||
"licence-third-party": "Für Lizenzen von Drittanbietermodulen, siehe",
|
||||
"licence-third-party-link": "Legal",
|
||||
"list-contributors": "Für eine vollstandige Liste aller Beteiligten und Dank, siehe",
|
||||
"list-contributors-link": "Credits",
|
||||
"version": "Version"
|
||||
},
|
||||
"config": {
|
||||
"main-tab": "Hauptmenü",
|
||||
"view-config-tab": "Konfiguration",
|
||||
"edit-config-tab": "Konfiguration bearbeiten",
|
||||
"custom-css-tab": "eigene CSS",
|
||||
"heading": "Konfigurationseinstellungen",
|
||||
"download-config-button": "Konfigurationsdownload",
|
||||
"edit-config-button": "Konfiguration bearbeiten",
|
||||
"edit-css-button": "CSS bearbeiten",
|
||||
"cloud-sync-button": "Cloud-Synchronisation aktivieren",
|
||||
"edit-cloud-sync-button": "Cloud-Synchronisation bearbeiten",
|
||||
"rebuild-app-button": "Anwendung neu kompilieren",
|
||||
"change-language-button": "App-Sprache ändern",
|
||||
"reset-settings-button": "lokale Einstellungen zurücksetzen",
|
||||
"disabled-note": "Einige Konfigurationsoptionen wurden vom Administrator deaktivert",
|
||||
"small-screen-note": "Sie benutzen einen sehr kleinen Bildschirm. Einige Seiten in diesem Menü sind dafür möglicherweise nicht ideal.",
|
||||
"app-info-button": "App Informationen",
|
||||
"backup-note": "Es wird empfohlen ein Backup der Konfiguration zu erstellen bevor Änderungen durchgeführt werden.",
|
||||
"reset-config-msg-l1": "Dadurch werden alle Benutzereinstellungen aus dem lokalen Speicher entfernt, dies hat jedoch keine Auswirkungen auf Ihre Datei 'conf.yml'.",
|
||||
"reset-config-msg-l2": "Sie sollten zuerst alle Änderungen, die Sie lokal vorgenommen haben, sichern, wenn Sie sie in Zukunft wiederverwenden möchten.",
|
||||
"reset-config-msg-l3": "Sind Sie sicher, dass Sie fortfahren möchten?",
|
||||
"data-cleared-msg": "Daten erfolgreich gelöscht",
|
||||
"actions-label": "Aktionen",
|
||||
"copy-config-label": "Konfiguration kopieren",
|
||||
"data-copied-msg": "Konfiguration wurde in die Zwischenablage kopiert",
|
||||
"reset-config-label": "Konfiguration zurücksetzen",
|
||||
"css-save-btn": "Änderungen speichern",
|
||||
"css-note-label": "Bemerkung",
|
||||
"css-note-l1": "Sie müssen die Seite aktualisieren, damit Ihre Änderungen wirksam werden.",
|
||||
"css-note-l2": "Stilüberschreibungen werden nur lokal gespeichert, daher wird empfohlen vorher eine Kopie Ihres CSS zu erstellen.",
|
||||
"css-note-l3": "Um alle benutzerdefinierten Stile zu entfernen löschen Sie den Inhalt und klicken Sie auf Änderungen speichern.",
|
||||
"custom-css": {
|
||||
"title": "Eigenes CSS",
|
||||
"base-theme": "Basisdesign"
|
||||
}
|
||||
},
|
||||
"alternate-views": {
|
||||
"alternate-view-heading": "Ansicht wechseln",
|
||||
"default": "Standard",
|
||||
"workspace": "Arbeitsplatz",
|
||||
"minimal": "Minimal"
|
||||
},
|
||||
"settings": {
|
||||
"theme-label": "Design",
|
||||
"layout-label": "Layout",
|
||||
"layout-auto": "Auto",
|
||||
"layout-horizontal": "Horizontal",
|
||||
"layout-vertical": "Vertikal",
|
||||
"item-size-label": "Itemgröße",
|
||||
"item-size-small": "klein",
|
||||
"item-size-medium": "mittel",
|
||||
"item-size-large": "groß",
|
||||
"config-launcher-label": "Konfiguration",
|
||||
"config-launcher-tooltip": "Konfiguration aktualisieren",
|
||||
"sign-out-tooltip": "Abmelden",
|
||||
"sign-in-tooltip": "Anmelden",
|
||||
"sign-in-welcome": "Hallo {username}!",
|
||||
"hide": "Verstecke",
|
||||
"open": "Öffne"
|
||||
},
|
||||
"updates": {
|
||||
"app-version-note": "Dashy Version",
|
||||
"up-to-date": "Aktuell",
|
||||
"out-of-date": "Update verfügbar",
|
||||
"unsupported-version-l1": "Sie verwenden eine nicht unterstützte Version von Dashy",
|
||||
"unsupported-version-l2": "Für die beste Erfahrung und aktuelle Sicherheitspatches aktualisieren Sie bitte auf"
|
||||
},
|
||||
"language-switcher": {
|
||||
"title": "Applikationssprache ändern",
|
||||
"dropdown-label": "Sprache auswählen",
|
||||
"save-button": "Speichern",
|
||||
"success-msg": "Sprache geändert auf"
|
||||
},
|
||||
"theme-maker": {
|
||||
"title": "Design Konfigurator",
|
||||
"export-button": "Benutzerdefinierte Variablen exportieren",
|
||||
"reset-button": "CSS zurücksetzen für",
|
||||
"show-all-button": "Alle Variablen anzeigen",
|
||||
"change-fonts-button": "Schriftart ändern",
|
||||
"save-button": "Speichern",
|
||||
"cancel-button": "Abbrechen",
|
||||
"saved-toast": "{theme} wurde erfolgreich aktualisiert",
|
||||
"copied-toast": "Designdaten für {theme} wurden in die Zwischenablage kopiert.",
|
||||
"reset-toast": "Benutzerdefinierte Farben für {theme} wurden entfernt"
|
||||
},
|
||||
"config-editor": {
|
||||
"save-location-label": "Speicherort",
|
||||
"location-local-label": "Lokal anwenden",
|
||||
"location-disk-label": "Änderungen in die Konfigurationsdatei schreiben",
|
||||
"save-button": "Änderungen speichern",
|
||||
"preview-button": "Vorschau der Änderungen",
|
||||
"valid-label": "Syntax ist gültig",
|
||||
"status-success-msg": "Aufgabe abgeschlossen",
|
||||
"status-fail-msg": "Aufgabe fehlgeschlagen",
|
||||
"success-msg-disk": "Konfigurationsdatei wurde erfolgreich auf die Festplatte geschrieben",
|
||||
"success-msg-local": "Lokale Änderungen wurden erfolgreich gespeichert",
|
||||
"success-note-l1": "Die Applikation sollte automatisch re-kompiliert werden.",
|
||||
"success-note-l2": "Dies kann bis zu einer Minute dauern.",
|
||||
"success-note-l3": "Sie müssen die Seite aktualisieren damit die Änderungen wirksam werden.",
|
||||
"error-msg-save-mode": "Bitte wählen Sie einen Speichermodus: Lokal oder Datei",
|
||||
"error-msg-cannot-save": "Beim Speichern der Konfiguration ist ein Fehler aufgetreten",
|
||||
"error-msg-bad-json": "Fehler in JSON-Daten, möglicherweise fehlerhafter Syntax",
|
||||
"warning-msg-validation": "Validierungswarnung",
|
||||
"not-admin-note": "Änderungen können nicht auf die Festplatte gespeichert werden, da Sie nicht als Administrator angemeldet sind"
|
||||
},
|
||||
"app-rebuild": {
|
||||
"title": "Applikation neu kompilieren",
|
||||
"rebuild-note-l1": "Damit die in die Datei conf.yml geschriebenen Änderungen wirksam werden ist ein Neukompilieren erforderlich.",
|
||||
"rebuild-note-l2": "Dies sollte automatisch passieren, aber falls nicht können Sie es hier manuell starten.",
|
||||
"rebuild-note-l3": "Dies ist bei lokal gespeicherten Änderungen nicht erforderlich.",
|
||||
"rebuild-button": "Starte Kompilierung",
|
||||
"rebuilding-status-1": "Baue...",
|
||||
"rebuilding-status-2": "Das kann ein paar Minuten dauern",
|
||||
"error-permission": "Sie sind nicht berechtigt diese Aktion auszulösen",
|
||||
"success-msg": "Kompilierung erfolgreich abgeschlossen",
|
||||
"fail-msg": "Kompilierung fehlgeschlagen",
|
||||
"reload-note": "Ein neu Laden der Seite ist erforderlich, damit die Änderungen wirksam werden.",
|
||||
"reload-button": "Seite neu laden"
|
||||
},
|
||||
"cloud-sync": {
|
||||
"title": "Cloud Backup & Wiederherstellung",
|
||||
"intro-l1": "Cloud-Backup und Wiederherstellung ist eine optionale Funktion mit der Sie Ihre Konfiguration in das Internet hochladen und dann auf einem anderen Gerät oder einer anderen Dashy-Instanz wiederherstellen können.",
|
||||
"intro-l2": "Alle Daten sind vollständig Ende-zu-Ende mit AES verschlüsselt. Ihr Passwort wird als Schlüssel verwendet.",
|
||||
"intro-l3": "Weitere Informationen finden Sie in der",
|
||||
"intro-docs": "Dokumentation",
|
||||
"backup-title-setup": "Backup erstellen",
|
||||
"backup-title-update": "Backup aktualisieren",
|
||||
"password-label-setup": "Passwort auswählen",
|
||||
"password-label-update": "Passwort eingeben",
|
||||
"backup-button-setup": "Backup",
|
||||
"backup-button-update": "Backup aktualisieren",
|
||||
"backup-id-label": "Ihre Backup ID",
|
||||
"backup-id-note": "Diese wird zusammen mit dem Passwort benötigt um Ihr Backup wiederherzustellen. Bewahren Sie sie zusammen mit Ihrem Passwort an einem sicheren Ort auf.",
|
||||
"restore-title": "Backup wiederherstellen",
|
||||
"restore-id-label": "ID wiederherstellen",
|
||||
"restore-password-label": "Passwort",
|
||||
"restore-button": "Wiederherstellen",
|
||||
"backup-missing-password": "Passwort fehlt",
|
||||
"backup-error-unknown": "Anfrage kann nicht verarbeitet werden",
|
||||
"backup-error-password": "Falsches Passwort. Bitte geben Sie Ihr aktuelles Passwort ein.",
|
||||
"backup-success-msg": "Erfolgreich abgeschlossen",
|
||||
"restore-success-msg": "Konfiguration erfolgreich wiederhergestellt"
|
||||
},
|
||||
"menu": {
|
||||
"open-section-title": "Öffne in",
|
||||
"sametab": "Aktueller Tab",
|
||||
"newtab": "Neuer Tab",
|
||||
"modal": "Popup Modal",
|
||||
"workspace": "Arbeitsflächenansicht",
|
||||
"options-section-title": "Optionen",
|
||||
"edit-item": "Bearbeiten",
|
||||
"move-item": "Kopieren oder Verschieben",
|
||||
"remove-item": "Entfernen"
|
||||
},
|
||||
"context-menus": {
|
||||
"item": {
|
||||
"open-section-title": "Öffnen in",
|
||||
"sametab": "Aktueller Tab",
|
||||
"newtab": "Neuer Tab",
|
||||
"modal": "Popup Modal",
|
||||
"workspace": "Arbeitsflächenansicht",
|
||||
"clipboard": "In Zwischenablage kopieren",
|
||||
"options-section-title": "Optionen",
|
||||
"edit-item": "Bearbeiten",
|
||||
"move-item": "Kopieren oder Verschieben",
|
||||
"remove-item": "Entfernen",
|
||||
"copied-toast": "URL wurde in die Zwischenablage kopiert"
|
||||
},
|
||||
"section": {
|
||||
"open-section": "Sektion öffnen",
|
||||
"edit-section": "Bearbeiten",
|
||||
"expand-collapse": "Aus- / Einklappen",
|
||||
"move-section": "Verschieben nach",
|
||||
"remove-section": "Entfernen"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"dev-by": "Entwickelt von",
|
||||
"licensed-under": "Lizensiert unter",
|
||||
"get-the": "Hole dir den",
|
||||
"source-code": "Quellcode"
|
||||
},
|
||||
"interactive-editor": {
|
||||
"menu": {
|
||||
"start-editing-tooltip": "Interaktiven Editor starten",
|
||||
"edit-site-data-subheading": "Seiteninformationen bearbeiten",
|
||||
"edit-page-info-btn": "Seiteninformationen bearbeiten",
|
||||
"edit-page-info-tooltip": "Applikationstitel, Beschreibung, Nav. links, Fußzeile, etc.",
|
||||
"edit-app-config-btn": "Applikationskonfiguration bearbeiten",
|
||||
"edit-app-config-tooltip": "Alle anderen Konfigurationsoptionen",
|
||||
"edit-pages-btn": "Seiten bearbeiten",
|
||||
"edit-pages-tooltip": "Hinzufügen oder entfernen von zusätzlichen Ansichten",
|
||||
"config-save-methods-subheading": "Speicheroptionen der Konfiguration",
|
||||
"save-locally-btn": "Lokal speichern",
|
||||
"save-locally-tooltip": "Konfiguration lokal im Browser speichern. Dies hat keinen Einfluss auf die Konfigurationsdatei, aber Änderungen werden nur in diesem Browser gespeichert",
|
||||
"save-disk-btn": "Auf Festplatte speichern",
|
||||
"save-disk-tooltip": "Konfigurationsdatei conf.yml speichern. Dies erzeugt ein Backup und überschreibt dann die existierende Konfigurationsdatei",
|
||||
"export-config-btn": "Konfiguration exportieren",
|
||||
"export-config-tooltip": "Konfiguration anzeigen und exportieren, entweder in eine Datei oder in die Zwischenablage",
|
||||
"cloud-backup-btn": "Cloud-Backup starten",
|
||||
"cloud-backup-tooltip": "Speichert ein verrschlüsseltes Backup in die Cloud",
|
||||
"edit-raw-config-btn": "Konfiguration als Rohdaten bearbeiten",
|
||||
"edit-raw-config-tooltip": "Anzeigen und bearbeiten der Konfiguration als Rohdaten im JSON-Editor",
|
||||
"cancel-changes-btn": "Verwerfen",
|
||||
"cancel-changes-tooltip": "Modifikationen zurücksetzen und Bearbeitungsmodus schließen. Dies hat keinen Einfluss auf die Konfigurationsdatei",
|
||||
"edit-mode-name": "Bearbeitung",
|
||||
"edit-mode-subtitle": "Sie sind im Bearbeitungsmodus",
|
||||
"edit-mode-description": "Das bedeutet, dass Änderungen an der Konfigurationsdatei vorgenommen werden können. Änderungen können vor dem Speichern betrachtet werden.",
|
||||
"save-stage-btn": "Speichern",
|
||||
"cancel-stage-btn": "Abbrechen",
|
||||
"save-locally-warning": "Wenn Sie fortfahren werden die Änderungen nur in Ihrem Browser gespeichert. Um die Konfiguration auf anderen Geräten zu nutzen sollten Sie sie exportieren. Möchten Sie fortfahren?"
|
||||
},
|
||||
"edit-item": {
|
||||
"missing-title-err": "Ein Titel is zwingend notwendig"
|
||||
},
|
||||
"edit-section": {
|
||||
"edit-section-title": "Sektion bearbeiten",
|
||||
"add-section-title": "Neue Sektion hinzufügen",
|
||||
"edit-tooltip": "Klicken zum Bearbeiten oder Rechtsklick für weitere Optionen",
|
||||
"remove-confirm": "Sind Sie sicher, dass sie diese Sektion entfernen möchten? Diese Aktion kann nicht rückgänging gemacht werden."
|
||||
},
|
||||
"edit-app-config": {
|
||||
"warning-msg-title": "Ab hier ist Vorsicht geboten",
|
||||
"warning-msg-l1": "Die folgenden Optionen sind für fortgeschrittene Konfigurationen.",
|
||||
"warning-msg-l2": "Sollten Felder unklar sein, konsultieren Sie die",
|
||||
"warning-msg-docs": "Dokumentation",
|
||||
"warning-msg-l3": "um unbeabsichtigte Folgen zu vermeiden."
|
||||
},
|
||||
"export": {
|
||||
"export-title": "Konfiguration exportieren",
|
||||
"copy-clipboard-btn": "In Zwischenablage kopieren",
|
||||
"copy-clipboard-tooltip": "Applikationskonfiguration als YAML in Zwischenablage kopieren",
|
||||
"download-file-btn": "Datei herunterladen",
|
||||
"download-file-tooltip": "Applikationskonfiguration auf Ihr Gerät herunterladen",
|
||||
"view-title": "Konfiguration anzeigen"
|
||||
}
|
||||
},
|
||||
"widgets": {
|
||||
"general": {
|
||||
"loading": "Lade...",
|
||||
"show-more": "Details",
|
||||
"cpu-details": "Details CPU",
|
||||
"mem-details": "Details Arbeitsspeicher",
|
||||
"show-less": "Weniger anzeigen",
|
||||
"open-link": "Weiterlesen"
|
||||
},
|
||||
"pi-hole": {
|
||||
"status-heading": "Status"
|
||||
},
|
||||
"stat-ping": {
|
||||
"up": "Online",
|
||||
"down": "Offline"
|
||||
},
|
||||
"net-data": {
|
||||
"cpu-chart-title": "CPU Historie",
|
||||
"mem-chart-title": "Speichernutzung",
|
||||
"mem-breakdown-title": "Speichernutzung",
|
||||
"load-chart-title": "Systemlast"
|
||||
},
|
||||
"glances": {
|
||||
"disk-space-free": "Frei",
|
||||
"disk-space-used": "Genutzt",
|
||||
"disk-mount-point": "Mountpunkt",
|
||||
"disk-file-system": "Dateisystem,",
|
||||
"disk-io-read": "Lesen",
|
||||
"disk-io-write": "Schreiben",
|
||||
"system-load-desc": "Prozesse in der Warteschlange, Durchschnitt aller Kerne"
|
||||
},
|
||||
"system-info": {
|
||||
"uptime": "Uptime"
|
||||
},
|
||||
"flight-data": {
|
||||
"arrivals": "Ankünfte",
|
||||
"departures": "Abflüge"
|
||||
},
|
||||
"tfl-status": {
|
||||
"good-service-all": "Guter Service auf allen Leitungen",
|
||||
"good-service-rest": "Guter Service auf allen anderen Leitungen"
|
||||
},
|
||||
"synology-download": {
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"downloaded": "Heruntergeladen",
|
||||
"uploaded": "Hochgeladen",
|
||||
"remaining": "Verbleibend",
|
||||
"up": "Hoch",
|
||||
"down": "Runter"
|
||||
},
|
||||
"gluetun-status": {
|
||||
"vpn-ip": "VPN IP",
|
||||
"country": "Land",
|
||||
"region": "Bundesland",
|
||||
"city": "Stadt",
|
||||
"post-code": "Postleitzahl",
|
||||
"location": "Standort",
|
||||
"timezone": "Zeitzone",
|
||||
"organization": "Organisation"
|
||||
},
|
||||
"nextcloud": {
|
||||
"active": "Aktiv",
|
||||
"and": "und",
|
||||
"applications": "Anwendungen",
|
||||
"available": "Verfügbar",
|
||||
"away": "Abwesend",
|
||||
"cache-full": "CACHE VOLL",
|
||||
"chat-room": "Chatraum",
|
||||
"delete-all": "Alle löschen",
|
||||
"delete-notification": "Benachrichtigung löschen",
|
||||
"disabled": "deaktivert",
|
||||
"disk-quota": "Disk Quota",
|
||||
"disk-space": "Disk Speicherplatz",
|
||||
"dnd": "Nicht stören",
|
||||
"email": "E-Mail",
|
||||
"enabled": "aktiviert",
|
||||
"federated-shares-ucfirst": "Föderierte Freigaben",
|
||||
"federated-shares": "föderierte Freigaben",
|
||||
"files": "Dateien",
|
||||
"free": "frei",
|
||||
"groups": "Gruppen",
|
||||
"hit-rate": "Trefferrate",
|
||||
"hits": "Treffer",
|
||||
"home": "Zuhause",
|
||||
"in": "in",
|
||||
"keys": "Schlüssel",
|
||||
"last-24-hours": "letzte 24 Stunden",
|
||||
"last-5-minutes": "in den letzten 5 Minuten",
|
||||
"last-hour": "in der letzten Stunde",
|
||||
"last-login": "Letzte Anmeldung",
|
||||
"last-restart": "Letzter Neustart",
|
||||
"load-averages": "Systemlast aller CPU-Kerne",
|
||||
"local-shares": "Lokale Freigaben",
|
||||
"local": "lokal",
|
||||
"max-keys": "Maximale Schlüssel",
|
||||
"memory-used": "Speichernutzung",
|
||||
"memory-utilisation": "Speichernutzung",
|
||||
"memory": "Speicher",
|
||||
"misses": "Fehlschläge",
|
||||
"no-notifications": "Keine Benachrichtigungen",
|
||||
"no-pending-updates": "keine ausstehenden Aktualisierungen",
|
||||
"nothing-to-show": "Momentan gibt es hier nichts zu zeigen",
|
||||
"of-which": "welche",
|
||||
"of": "von",
|
||||
"offline": "Offline",
|
||||
"online": "Online",
|
||||
"other": "andere",
|
||||
"overall": "Insgesamt",
|
||||
"private-link": "privater Link",
|
||||
"public-link": "öffentlicher Link",
|
||||
"quota-enabled": "Disk Quota ist {nicht}aktiviert für diesen Benutzer",
|
||||
"received": "empfangen",
|
||||
"scripts": "Skripte",
|
||||
"sent": "gesendet",
|
||||
"started": "gestartet",
|
||||
"storages-by-type": "Speicher nach Typ",
|
||||
"storages": "Speicher",
|
||||
"strings-use": "Strings benutzen",
|
||||
"tasks": "Aufgaben",
|
||||
"total-files": "Dateien gesamt",
|
||||
"total-users": "Benutzer gesamt",
|
||||
"total": "insgesamt",
|
||||
"until": "Bis",
|
||||
"updates-available-for": "Aktualisierungen sind verfügbar für",
|
||||
"updates-available": "Aktualisierungen verfügbar",
|
||||
"used": "benutzt",
|
||||
"user": "Benutzer",
|
||||
"using": "nutzt",
|
||||
"version": "Version",
|
||||
"wasted": "verschwendet"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,8 +63,8 @@
|
||||
"privacy-and-security": "Privacy & Security",
|
||||
"privacy-and-security-l1": "For a break-down of how your data is managed by Dashy, see the",
|
||||
"privacy-and-security-privacy-policy": "Privacy Policy",
|
||||
"app-info.privacy-and-security-advice": "For advise in securing your dashboard, you can reference the",
|
||||
"app-info.privacy-and-security-advice-link": "Management Docs",
|
||||
"privacy-and-security-advice": "For advise in securing your dashboard, you can reference the",
|
||||
"privacy-and-security-advice-link": "Management Docs",
|
||||
"privacy-and-security-security-issue": "If you've found a potential security issue, report it following our",
|
||||
"privacy-and-security-security-policy": "Security Policy",
|
||||
"license": "License",
|
||||
|
@ -63,8 +63,8 @@
|
||||
"privacy-and-security": "Privacidade e seguridade",
|
||||
"privacy-and-security-l1": "Para obter detalles sobre como os teus datos son xestionados por Dashy, consulta a",
|
||||
"privacy-and-security-privacy-policy": "Política de privacidade",
|
||||
"app-info.privacy-and-security-advice": "Para consellos sobre como asegurar o teu panel de control, consulta os",
|
||||
"app-info.privacy-and-security-advice-link": "Documentos de xestión",
|
||||
"privacy-and-security-advice": "Para consellos sobre como asegurar o teu panel de control, consulta os",
|
||||
"privacy-and-security-advice-link": "Documentos de xestión",
|
||||
"privacy-and-security-security-issue": "Se atopaches un problema de seguridade potencial, informa seguindo a nosa",
|
||||
"privacy-and-security-security-policy": "Política de seguridade",
|
||||
"license": "Licenza",
|
||||
|
@ -63,8 +63,8 @@
|
||||
"privacy-and-security": "プライバシーとセキュリティー",
|
||||
"privacy-and-security-l1": "Daisyがどのようにあなたのデータを管理するかについての概要は、こちらをご覧ください: ",
|
||||
"privacy-and-security-privacy-policy": "プライバシーポリシー",
|
||||
"app-info.privacy-and-security-advice": "ダッシュボードを安全に保つためのアドバイスは、こちらを参照してください: ",
|
||||
"app-info.privacy-and-security-advice-link": "管理ドキュメント",
|
||||
"privacy-and-security-advice": "ダッシュボードを安全に保つためのアドバイスは、こちらを参照してください: ",
|
||||
"privacy-and-security-advice-link": "管理ドキュメント",
|
||||
"privacy-and-security-security-issue": "セキュリティー問題の可能性のある事象を見つけた場合、セキュリティーポリシーに従ってください: ",
|
||||
"privacy-and-security-security-policy": "セキュリティーポリシー",
|
||||
"license": "ライセンス",
|
||||
|
448
src/assets/locales/ro.json
Normal file
448
src/assets/locales/ro.json
Normal file
@ -0,0 +1,448 @@
|
||||
{
|
||||
"home": {
|
||||
"no-results": "Niciun rezultat găsit",
|
||||
"no-data": "Nicio dată configurată",
|
||||
"no-items-section": "Niciun element de afișat încă"
|
||||
},
|
||||
"search": {
|
||||
"search-label": "Caută",
|
||||
"search-placeholder": "Începeți să tastați pentru a filtra",
|
||||
"clear-search-tooltip": "Șterge căutarea",
|
||||
"enter-to-search-web": "Apasă enter pentru a căuta pe web"
|
||||
},
|
||||
"splash-screen": {
|
||||
"loading": "Încărcare"
|
||||
},
|
||||
"login": {
|
||||
"title": "Dashy",
|
||||
"guest-label": "Acces Vizitator",
|
||||
"username-label": "Nume utilizator",
|
||||
"password-label": "Parolă",
|
||||
"login-button": "Autentificare",
|
||||
"remember-me-label": "Ține-mă minte pentru",
|
||||
"remember-me-never": "Niciodată",
|
||||
"remember-me-hour": "4 Ore",
|
||||
"remember-me-day": "1 Zi",
|
||||
"remember-me-week": "1 Săptămână",
|
||||
"remember-me-long-time": "O perioadă lungă",
|
||||
"error-missing-username": "Lipsește numele de utilizator",
|
||||
"error-missing-password": "Lipsește parola",
|
||||
"error-incorrect-username": "Utilizator negăsit",
|
||||
"error-incorrect-password": "Parolă incorectă",
|
||||
"success-message": "Autentificare...",
|
||||
"logout-message": "Deconectat",
|
||||
"already-logged-in-title": "Deja autentificat",
|
||||
"already-logged-in-text": "Ești autentificat ca",
|
||||
"proceed-to-dashboard": "Continuă către Tabloul de bord",
|
||||
"log-out-button": "Deconectare",
|
||||
"proceed-guest-button": "Continuă ca Vizitator",
|
||||
"guest-intro-1": "Această instanță are acces pentru vizitatori activat.",
|
||||
"guest-intro-2": "Vizitatorii au acces doar pentru vizualizare la tablourile de bord, deci nu pot scrie modificări pe disc.",
|
||||
"error": "Eroare",
|
||||
"error-no-user-configured": "Autentificarea nu este activată, sau nu au fost configurați utilizatori",
|
||||
"error-go-home-button": "Mergi la Pagina Principală",
|
||||
"logged-in-guest": "Autentificat ca Vizitator, Redirecționare...",
|
||||
"error-guest-access": "Acces Vizitator Interzis"
|
||||
},
|
||||
"app-info": {
|
||||
"title": "Informații Aplicație",
|
||||
"error-log": "Jurnal Erori",
|
||||
"no-errors": "Nicio eroare recentă detectată",
|
||||
"help-support": "Ajutor & Suport",
|
||||
"help-support-description" : "Pentru suport în utilizarea sau configurarea Dashy, consultați",
|
||||
"help-support-discussions": "Discuțiile",
|
||||
"support-dashy": "Suport pentru Dashy",
|
||||
"support-dashy-description": "Pentru moduri în care poți contribui, verificați",
|
||||
"support-dashy-link": "Pagina de Contribuții",
|
||||
"report-bug": "Raportează o Eroare",
|
||||
"report-bug-description": "Dacă crezi că ai găsit o eroare, atunci te rog",
|
||||
"report-bug-link": "deschide o Problemă",
|
||||
"more-info": "Mai Multe Informații",
|
||||
"source": "Sursă",
|
||||
"documentation": "Documentație",
|
||||
"privacy-and-security": "Confidențialitate & Securitate",
|
||||
"privacy-and-security-l1": "Pentru o descompunere a modului în care datele tale sunt gestionate de Dashy, consultați",
|
||||
"privacy-and-security-privacy-policy": "Politica de Confidențialitate",
|
||||
"privacy-and-security-advice": "Pentru sfaturi în securizarea tabloului tău de bord, poți consulta",
|
||||
"privacy-and-security-advice-link": "Documentele de Management",
|
||||
"privacy-and-security-security-issue": "Dacă ați descoperit o potențială problemă de securitate, raportați-o urmând",
|
||||
"privacy-and-security-security-policy": "Politica de Securitate",
|
||||
"license": "Licență",
|
||||
"license-under": "Licențiat sub",
|
||||
"licence-third-party": "Pentru licențele modulelor terțe părți, vă rugăm să consultați",
|
||||
"licence-third-party-link": "Legal",
|
||||
"list-contributors": "Pentru lista completă a contribuitorilor și mulțumiri, vedeți",
|
||||
"list-contributors-link": "Credite",
|
||||
"version": "Versiune"
|
||||
},
|
||||
"config": {
|
||||
"main-tab": "Meniu Principal",
|
||||
"view-config-tab": "Vizualizare Configurație",
|
||||
"edit-config-tab": "Editare Configurație",
|
||||
"custom-css-tab": "Stiluri Personalizate",
|
||||
"heading": "Opțiuni de Configurare",
|
||||
"download-config-button": "Vizualizați / Exportați Configurația",
|
||||
"edit-config-button": "Editare Configurație",
|
||||
"edit-css-button": "Editare CSS Personalizat",
|
||||
"cloud-sync-button": "Activează Sincronizarea în Cloud",
|
||||
"edit-cloud-sync-button": "Editare Sincronizare în Cloud",
|
||||
"rebuild-app-button": "Reconstruire Aplicație",
|
||||
"change-language-button": "Schimbă Limba Aplicației",
|
||||
"reset-settings-button": "Resetează Setările Locale",
|
||||
"disabled-note": "Unele caracteristici de configurare au fost dezactivate de administratorul tău",
|
||||
"small-screen-note": "Utilizați un ecran foarte mic, și unele ecrane din acest meniu s-ar putea să nu fie optimale",
|
||||
"app-info-button": "Informații Aplicație",
|
||||
"backup-note": "Este recomandat să faceți o copie de siguranță a configurației înainte de a face modificări.",
|
||||
"reset-config-msg-l1": "Aceasta va elimina toate setările utilizatorilor din stocarea locală, dar nu va afecta fișierul 'conf.yml'.",
|
||||
"reset-config-msg-l2": "Ar trebui să faceți mai întâi o copie de siguranță a oricăror modificări pe care le-ați făcut local, dacă doriți să le utilizați în viitor.",
|
||||
"reset-config-msg-l3": "Sunteți sigur că doriți să continuați?",
|
||||
"data-cleared-msg": "Datele au fost șterse cu succes",
|
||||
"actions-label": "Acțiuni",
|
||||
"copy-config-label": "Copiază Configurația",
|
||||
"data-copied-msg": "Configurația a fost copiată în clipboard",
|
||||
"reset-config-label": "Resetează Configurația",
|
||||
"css-save-btn": "Salvează Modificările",
|
||||
"css-note-label": "Notă",
|
||||
"css-note-l1": "Va trebui să reîmprospătați pagina pentru ca modificările să aibă efect.",
|
||||
"css-note-l2": "Suprascrierile de stiluri sunt stocate doar local, deci este recomandat să faceți o copie a CSS-ului dvs.",
|
||||
"css-note-l3": "Pentru a elimina toate stilurile personalizate, ștergeți conținutul și apăsați 'Salvează Modificările'",
|
||||
"custom-css": {
|
||||
"title": "CSS Personalizat",
|
||||
"base-theme": "Tema de Bază"
|
||||
}
|
||||
},
|
||||
"alternate-views": {
|
||||
"alternate-view-heading": "Schimbă Vederea",
|
||||
"default": "Implicit",
|
||||
"workspace": "Spațiu de Lucru",
|
||||
"minimal": "Minimal"
|
||||
},
|
||||
"settings": {
|
||||
"theme-label": "Temă",
|
||||
"layout-label": "Aspect",
|
||||
"layout-auto": "Automat",
|
||||
"layout-horizontal": "Orizontal",
|
||||
"layout-vertical": "Vertical",
|
||||
"item-size-label": "Dimensiune Element",
|
||||
"item-size-small": "Mic",
|
||||
"item-size-medium": "Mediu",
|
||||
"item-size-large": "Mare",
|
||||
"config-launcher-label": "Config",
|
||||
"config-launcher-tooltip": "Actualizează Configurația",
|
||||
"sign-out-tooltip": "Deconectare",
|
||||
"sign-in-tooltip": "Conectare",
|
||||
"sign-in-welcome": "Bună {username}!",
|
||||
"hide": "Ascunde",
|
||||
"open": "Deschide"
|
||||
},
|
||||
"updates": {
|
||||
"app-version-note": "Versiune Dashy",
|
||||
"up-to-date": "Actualizat",
|
||||
"out-of-date": "Actualizare Disponibilă",
|
||||
"unsupported-version-l1": "Utilizați o versiune nesuportată de Dashy",
|
||||
"unsupported-version-l2": "Pentru cea mai bună experiență și patch-uri de securitate recente, vă rugăm să actualizați la"
|
||||
},
|
||||
"language-switcher": {
|
||||
"title": "Schimbă Limba Aplicației",
|
||||
"dropdown-label": "Selectați o Limbă",
|
||||
"save-button": "Salvează",
|
||||
"success-msg": "Limba Actualizată la"
|
||||
},
|
||||
"theme-maker": {
|
||||
"title": "Configurator de Temă",
|
||||
"export-button": "Exportă Variabile Personalizate",
|
||||
"reset-button": "Resetează Stilurile pentru",
|
||||
"show-all-button": "Arată Toate Variabilele",
|
||||
"change-fonts-button": "Schimbă Fonturile",
|
||||
"save-button": "Salvează",
|
||||
"cancel-button": "Anulează",
|
||||
"saved-toast": "{theme} Actualizat cu Succes",
|
||||
"copied-toast": "Datele temei pentru {theme} au fost copiate în clipboard",
|
||||
"reset-toast": "Culorile Personalizate pentru {theme} au fost Eliminate"
|
||||
},
|
||||
"config-editor": {
|
||||
"save-location-label": "Locația de Salvare",
|
||||
"location-local-label": "Aplică Local",
|
||||
"location-disk-label": "Scrie Modificările în Fișierul de Configurație",
|
||||
"save-button": "Salvează Modificările",
|
||||
"preview-button": "Previzualizează Modificările",
|
||||
"valid-label": "Configurația este Valabilă",
|
||||
"status-success-msg": "Sarcină Completată",
|
||||
"status-fail-msg": "Sarcină Eșuată",
|
||||
"success-msg-disk": "Fișierul de configurație a fost scris pe disc cu succes",
|
||||
"success-msg-local": "Modificările locale au fost salvate cu succes",
|
||||
"success-note-l1": "Aplicația ar trebui să se reconstruiască automat.",
|
||||
"success-note-l2": "Aceasta poate dura până la un minut.",
|
||||
"success-note-l3": "Va trebui să reîmprospătați pagina pentru ca modificările să aibă efect.",
|
||||
"error-msg-save-mode": "Vă rugăm să selectați un Mod de Salvare: Local sau Fișier",
|
||||
"error-msg-cannot-save": "A apărut o eroare la salvarea configurației",
|
||||
"error-msg-bad-json": "Eroare în JSON, posibil malformat",
|
||||
"warning-msg-validation": "Avertisment de Validare",
|
||||
"not-admin-note": "Nu puteți scrie modificările pe disc deoarece nu sunteți conectat ca administrator"
|
||||
},
|
||||
"app-rebuild": {
|
||||
"title": "Reconstruire Aplicație",
|
||||
"rebuild-note-l1": "O reconstruire este necesară pentru ca modificările scrise în fișierul conf.yml să aibă efect.",
|
||||
"rebuild-note-l2": "Aceasta ar trebui să se întâmple automat, dar dacă nu s-a întâmplat, o puteți declanșa manual aici.",
|
||||
"rebuild-note-l3": "Aceasta nu este necesară pentru modificările stocate local.",
|
||||
"rebuild-button": "Începeți Reconstruirea",
|
||||
"rebuilding-status-1": "Se reconstruiește...",
|
||||
"rebuilding-status-2": "Aceasta poate dura câteva minute",
|
||||
"error-permission": "Nu aveți permisiunea de a declanșa această acțiune",
|
||||
"success-msg": "Reconstruirea a fost completată cu succes",
|
||||
"fail-msg": "Operațiunea de reconstruire a eșuat",
|
||||
"reload-note": "Este necesară reîncărcarea paginii pentru ca modificările să aibă efect",
|
||||
"reload-button": "Reîncarcă Pagina"
|
||||
},
|
||||
"cloud-sync": {
|
||||
"title": "Backup și Restaurare în Cloud",
|
||||
"intro-l1": "Backup-ul și restaurarea în cloud este o caracteristică opțională, care vă permite să încărcați configurația pe internet și apoi să o restaurați pe orice alt dispozitiv sau instanță de Dashy.",
|
||||
"intro-l2": "Toate datele sunt criptate end-to-end cu AES, folosind parola dumneavoastră ca cheie.",
|
||||
"intro-l3": "Pentru mai multe informații, vă rugăm să consultați",
|
||||
"intro-docs": "documentația",
|
||||
"backup-title-setup": "Creați un Backup",
|
||||
"backup-title-update": "Actualizați Backup-ul",
|
||||
"password-label-setup": "Alegeți o Parolă",
|
||||
"password-label-update": "Introduceți Parola",
|
||||
"backup-button-setup": "Backup",
|
||||
"backup-button-update": "Actualizați Backup-ul",
|
||||
"backup-id-label": "ID-ul Dvs. de Backup",
|
||||
"backup-id-note": "Acesta este utilizat pentru a restaura din backup-uri mai târziu. Deci păstrați-l, împreună cu parola dvs., într-un loc sigur.",
|
||||
"restore-title": "Restaurare Backup",
|
||||
"restore-id-label": "ID de Restaurare",
|
||||
"restore-password-label": "Parola",
|
||||
"restore-button": "Restaurare",
|
||||
"backup-missing-password": "Lipsește Parola",
|
||||
"backup-error-unknown": "Imposibil de procesat solicitarea",
|
||||
"backup-error-password": "Parolă incorectă. Vă rugăm să introduceți parola actuală.",
|
||||
"backup-success-msg": "Finalizat cu Succes",
|
||||
"restore-success-msg": "Configurația a fost Restaurată cu Succes"
|
||||
},
|
||||
"menu": {
|
||||
"open-section-title": "Deschide În",
|
||||
"sametab": "Tab Curent",
|
||||
"newtab": "Tab Nou",
|
||||
"modal": "Modal Pop-Up",
|
||||
"workspace": "Vizualizare Spațiu de Lucru",
|
||||
"options-section-title": "Opțiuni",
|
||||
"edit-item": "Editare",
|
||||
"move-item": "Copiază sau Mută",
|
||||
"remove-item": "Șterge"
|
||||
},
|
||||
"context-menus": {
|
||||
"item": {
|
||||
"open-section-title": "Deschide În",
|
||||
"sametab": "Tab Curent",
|
||||
"newtab": "Tab Nou",
|
||||
"modal": "Modal Pop-Up",
|
||||
"workspace": "Vizualizare Spațiu de Lucru",
|
||||
"clipboard": "Copiază în Clipboard",
|
||||
"options-section-title": "Opțiuni",
|
||||
"edit-item": "Editare",
|
||||
"move-item": "Copiază sau Mută",
|
||||
"remove-item": "Șterge",
|
||||
"copied-toast": "URL-ul a fost copiat în clipboard"
|
||||
},
|
||||
"section": {
|
||||
"open-section": "Deschide Secțiunea",
|
||||
"edit-section": "Editare",
|
||||
"expand-collapse": "Extinde / Colapsează",
|
||||
"move-section": "Mută La",
|
||||
"remove-section": "Șterge"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"dev-by": "Dezvoltat de",
|
||||
"licensed-under": "Licențiat sub",
|
||||
"get-the": "Obțineți",
|
||||
"source-code": "Codul Sursă"
|
||||
},
|
||||
"interactive-editor": {
|
||||
"menu": {
|
||||
"start-editing-tooltip": "Intră în Editorul Interactiv",
|
||||
"edit-site-data-subheading": "Editează Datele Site-ului",
|
||||
"edit-page-info-btn": "Editează Informațiile Paginii",
|
||||
"edit-page-info-tooltip": "Titlul aplicației, descriere, link-uri de navigare, textul din footer, etc.",
|
||||
"edit-app-config-btn": "Editează Configurația Aplicației",
|
||||
"edit-app-config-tooltip": "Toate celelalte opțiuni de configurare a aplicației",
|
||||
"edit-pages-btn": "Editează Paginile",
|
||||
"edit-pages-tooltip": "Adaugă sau elimină vizualizări suplimentare",
|
||||
"config-save-methods-subheading": "Opțiuni de Salvare a Configurației",
|
||||
"save-locally-btn": "Salvează Local",
|
||||
"save-locally-tooltip": "Salvează configurația local, în stocarea browserului. Aceasta nu va afecta fișierul tău de configurare, dar schimbările vor fi salvate doar pe acest dispozitiv",
|
||||
"save-disk-btn": "Salvează pe Disc",
|
||||
"save-disk-tooltip": "Salvează configurația în fișierul conf.yml de pe disc. Aceasta va face un backup și apoi va suprascrie configurația existentă",
|
||||
"export-config-btn": "Exportă Configurația",
|
||||
"export-config-tooltip": "Vizualizează și exportă noua configurație, fie într-un fișier, fie în clipboard",
|
||||
"cloud-backup-btn": "Backup în Cloud",
|
||||
"cloud-backup-tooltip": "Salvează un backup criptat al configurației în cloud",
|
||||
"edit-raw-config-btn": "Editează Configurația Brută",
|
||||
"edit-raw-config-tooltip": "Vizualizează și modifică configurația brută prin editorul JSON",
|
||||
"cancel-changes-btn": "Anulează Modificările",
|
||||
"cancel-changes-tooltip": "Resetează modificările curente și ieși din Modul de Editare. Aceasta nu va afecta configurația salvată",
|
||||
"edit-mode-name": "Mod de Editare",
|
||||
"edit-mode-subtitle": "Ești în Modul de Editare",
|
||||
"edit-mode-description": "Aceasta înseamnă că poți face modificări la configurația ta și să previzualizezi rezultatele, dar până nu salvezi, niciuna dintre modificările tale nu va fi păstrată.",
|
||||
"save-stage-btn": "Salvează",
|
||||
"cancel-stage-btn": "Anulează",
|
||||
"save-locally-warning": "Dacă vei continua, modificările vor fi salvate doar în browserul tău. Ar trebui să exporți o copie a configurației tale pentru utilizare pe alte mașini. Dorești să continui?"
|
||||
},
|
||||
"edit-item": {
|
||||
"missing-title-err": "Este necesar un titlu pentru element"
|
||||
},
|
||||
"edit-section": {
|
||||
"edit-section-title": "Editează Secțiunea",
|
||||
"add-section-title": "Adaugă o Secțiune Nouă",
|
||||
"edit-tooltip": "Clic pentru a edita, sau clic dreapta pentru mai multe opțiuni",
|
||||
"remove-confirm": "Ești sigur că vrei să elimini această secțiune? Această acțiune poate fi anulată mai târziu."
|
||||
},
|
||||
"edit-app-config": {
|
||||
"warning-msg-title": "Procedează cu Atenție",
|
||||
"warning-msg-l1": "Următoarele opțiuni sunt pentru configurarea avansată a aplicației.",
|
||||
"warning-msg-l2": "Dacă nu ești sigur în legătură cu oricare dintre câmpuri, te rog să consulți",
|
||||
"warning-msg-docs": "documentația",
|
||||
"warning-msg-l3": "pentru a evita consecințele nedorite."
|
||||
},
|
||||
"export": {
|
||||
"export-title": "Exportă Configurația",
|
||||
"copy-clipboard-btn": "Copiază în Clipboard",
|
||||
"copy-clipboard-tooltip": "Copiază toată configurația aplicației în clipboard-ul sistemului, în format YAML",
|
||||
"download-file-btn": "Descarcă ca Fișier",
|
||||
"download-file-tooltip": "Descarcă toată configurația aplicației pe dispozitivul tău, într-un fișier YAML",
|
||||
"view-title": "Vizualizează Configurația"
|
||||
}
|
||||
},
|
||||
"widgets": {
|
||||
"general": {
|
||||
"loading": "Se încarcă...",
|
||||
"show-more": "Extinde Detaliile",
|
||||
"cpu-details": "Detalii CPU",
|
||||
"mem-details": "Detalii Memorie",
|
||||
"show-less": "Arată Mai Puțin",
|
||||
"open-link": "Continuă Citirea"
|
||||
},
|
||||
"pi-hole": {
|
||||
"status-heading": "Stare"
|
||||
},
|
||||
"stat-ping": {
|
||||
"up": "Online",
|
||||
"down": "Offline"
|
||||
},
|
||||
"net-data": {
|
||||
"cpu-chart-title": "Istoric CPU",
|
||||
"mem-chart-title": "Utilizare Memorie",
|
||||
"mem-breakdown-title": "Detalii Memorie",
|
||||
"load-chart-title": "Încărcare Sistem"
|
||||
},
|
||||
"glances": {
|
||||
"disk-space-free": "Liber",
|
||||
"disk-space-used": "Utilizat",
|
||||
"disk-mount-point": "Punct de Montare",
|
||||
"disk-file-system": "Sistem de Fișiere",
|
||||
"disk-io-read": "Citire",
|
||||
"disk-io-write": "Scriere",
|
||||
"system-load-desc": "Numărul de procese așteptând în coada de execuție, mediat pe toate nucleele"
|
||||
},
|
||||
"system-info": {
|
||||
"uptime": "Timp de Funcționare"
|
||||
},
|
||||
"flight-data": {
|
||||
"arrivals": "Sosiri",
|
||||
"departures": "Plecări"
|
||||
},
|
||||
"tfl-status": {
|
||||
"good-service-all": "Servicii Bune pe Toate Liniile",
|
||||
"good-service-rest": "Servicii Bune pe Celelalte Linii"
|
||||
},
|
||||
"synology-download": {
|
||||
"download": "Descărcare",
|
||||
"upload": "Încărcare",
|
||||
"downloaded": "Descărcat",
|
||||
"uploaded": "Încărcat",
|
||||
"remaining": "Rămas",
|
||||
"up": "Sus",
|
||||
"down": "Jos"
|
||||
},
|
||||
"gluetun-status": {
|
||||
"vpn-ip": "IP VPN",
|
||||
"country": "Țară",
|
||||
"region": "Regiune",
|
||||
"city": "Oraș",
|
||||
"post-code": "Cod Poștal",
|
||||
"location": "Locație",
|
||||
"timezone": "Fus Orar",
|
||||
"organization": "Organizație"
|
||||
},
|
||||
"nextcloud": {
|
||||
"active": "activ",
|
||||
"and": "și",
|
||||
"applications": "aplicații",
|
||||
"available": "disponibil",
|
||||
"away": "Plecat",
|
||||
"cache-full": "CACHE PLIN",
|
||||
"chat-room": "camera de chat",
|
||||
"delete-all": "Șterge tot",
|
||||
"delete-notification": "Șterge notificarea",
|
||||
"disabled": "dezactivat",
|
||||
"disk-quota": "Cotă de Disc",
|
||||
"disk-space": "Spațiu pe Disc",
|
||||
"dnd": "Nu Deranja",
|
||||
"email": "email",
|
||||
"enabled": "activat",
|
||||
"federated-shares-ucfirst": "Partajări Federate",
|
||||
"federated-shares": "partajări federate",
|
||||
"files": "fișier{plural}",
|
||||
"free": "liber",
|
||||
"groups": "grupuri",
|
||||
"hit-rate": "rata de accesare",
|
||||
"hits": "accesări",
|
||||
"home": "acasă",
|
||||
"in": "în",
|
||||
"keys": "chei",
|
||||
"last-24-hours": "ultimele 24 de ore",
|
||||
"last-5-minutes": "în ultimele 5 minute",
|
||||
"last-hour": "în ultima oră",
|
||||
"last-login": "Ultima autentificare",
|
||||
"last-restart": "Ultimul restart",
|
||||
"load-averages": "Mediile de Încărcare pe toate nucleele CPU",
|
||||
"local-shares": "Partajări Locale",
|
||||
"local": "local",
|
||||
"max-keys": "chei maxime",
|
||||
"memory-used": "memorie utilizată",
|
||||
"memory-utilisation": "utilizarea memoriei",
|
||||
"memory": "memorie",
|
||||
"misses": "rateuri",
|
||||
"no-notifications": "Fără notificări",
|
||||
"no-pending-updates": "fără actualizări în așteptare",
|
||||
"nothing-to-show": "Nimic de afișat aici în acest moment",
|
||||
"of-which": "din care",
|
||||
"of": "din",
|
||||
"offline": "Deconectat",
|
||||
"online": "Conectat",
|
||||
"other": "alt",
|
||||
"overall": "În total",
|
||||
"private-link": "link privat",
|
||||
"public-link": "link public",
|
||||
"quota-enabled": "Cota de Disc este {not}activată pentru acest utilizator",
|
||||
"received": "primit",
|
||||
"scripts": "scripturi",
|
||||
"sent": "trimis",
|
||||
"started": "Început",
|
||||
"storages-by-type": "Stocări pe tip",
|
||||
"storages": "stocare{plural}",
|
||||
"strings-use": "utilizare șiruri",
|
||||
"tasks": "Sarcini",
|
||||
"total-files": "total fișiere",
|
||||
"total-users": "total utilizatori",
|
||||
"total": "total",
|
||||
"until": "Până la",
|
||||
"updates-available-for": "Actualizări disponibile pentru",
|
||||
"updates-available": "actualizare{plural} disponibilă",
|
||||
"used": "utilizat",
|
||||
"user": "utilizator",
|
||||
"using": "utilizând",
|
||||
"version": "versiune",
|
||||
"wasted": "pierdut"
|
||||
}
|
||||
}
|
||||
}
|
@ -63,8 +63,8 @@
|
||||
"privacy-and-security": "Конфіденційність та безпека",
|
||||
"privacy-and-security-l1": "Для детальної інформації про те, як Dashy керує вашими даними, див.",
|
||||
"privacy-and-security-privacy-policy": "Політика конфіденційності",
|
||||
"app-info.privacy-and-security-advice": "Щоб отримати поради щодо захисту вашої інформаційної панелі, ви можете звернутися до розділу",
|
||||
"app-info.privacy-and-security-advice-link": "Документи керування",
|
||||
"privacy-and-security-advice": "Щоб отримати поради щодо захисту вашої інформаційної панелі, ви можете звернутися до розділу",
|
||||
"privacy-and-security-advice-link": "Документи керування",
|
||||
"privacy-and-security-security-issue": "Якщо ви виявили потенційну проблему з безпекою, повідомте про це до розділу",
|
||||
"privacy-and-security-security-policy": "Політика безпеки",
|
||||
"license": "Ліцензія",
|
||||
|
@ -63,8 +63,8 @@
|
||||
"privacy-and-security": "隐私与安全",
|
||||
"privacy-and-security-l1": "关于Dashy如何管理您的数据的详细信息,请参阅",
|
||||
"privacy-and-security-privacy-policy": "隐私政策",
|
||||
"app-info.privacy-and-security-advice": "如果您需要保护您的仪表盘,请参考",
|
||||
"app-info.privacy-and-security-advice-link": "管理文档",
|
||||
"privacy-and-security-advice": "如果您需要保护您的仪表盘,请参考",
|
||||
"privacy-and-security-advice-link": "管理文档",
|
||||
"privacy-and-security-security-issue": "如果您发现潜在的安全问题,请遵循我们的",
|
||||
"privacy-and-security-security-policy": "安全政策",
|
||||
"license": "许可证",
|
||||
|
@ -63,8 +63,8 @@
|
||||
"privacy-and-security": "隱私權和安全性",
|
||||
"privacy-and-security-l1": "若要了解 Dashy 是如何管理您的資料,請參閱",
|
||||
"privacy-and-security-privacy-policy": "隱私權政策",
|
||||
"app-info.privacy-and-security-advice": "關於提升儀錶板安全性的建議,請參閱",
|
||||
"app-info.privacy-and-security-advice-link": "管理文件",
|
||||
"privacy-and-security-advice": "關於提升儀錶板安全性的建議,請參閱",
|
||||
"privacy-and-security-advice-link": "管理文件",
|
||||
"privacy-and-security-security-issue": "若您找到潛在的安全問題,請回報,並遵照我們的",
|
||||
"privacy-and-security-security-policy": "安全政策",
|
||||
"license": "授權條款",
|
||||
|
@ -30,7 +30,7 @@
|
||||
<!-- License -->
|
||||
<h3>{{ $t('app-info.license') }}</h3>
|
||||
{{ $t('app-info.license-under') }} <a href="https://github.com/Lissy93/dashy/blob/master/LICENSE">MIT X11</a>.
|
||||
Copyright <a href="https://aliciasykes.com">Alicia Sykes</a> © 2021.<br>
|
||||
Copyright <a href="https://aliciasykes.com">Alicia Sykes</a> © {{new Date().getFullYear()}}.<br>
|
||||
{{ $t('app-info.licence-third-party') }} <a href="https://github.com/Lissy93/dashy/blob/master/.github/LEGAL.md">{{ $t('app-info.licence-third-party-link') }}</a>.<br>
|
||||
{{ $t('app-info.list-contributors') }} <a href="https://github.com/Lissy93/dashy/blob/master/docs/credits.md">{{ $t('app-info.list-contributors-link') }}</a>.
|
||||
<!-- App Version -->
|
||||
|
@ -155,12 +155,23 @@ export default {
|
||||
},
|
||||
/* When restored data is revieved, then save to local storage, and apply it in state */
|
||||
applyRestoredData(config, backupId) {
|
||||
// Store restored data in local storage
|
||||
localStorage.setItem(localStorageKeys.CONF_SECTIONS, JSON.stringify(config.sections));
|
||||
localStorage.setItem(localStorageKeys.APP_CONFIG, JSON.stringify(config.appConfig));
|
||||
localStorage.setItem(localStorageKeys.PAGE_INFO, JSON.stringify(config.pageInfo));
|
||||
if (config.appConfig.theme) {
|
||||
localStorage.setItem(localStorageKeys.THEME, config.appConfig.theme);
|
||||
const isSubPage = !!this.$store.state.currentConfigInfo.confId;
|
||||
if (isSubPage) { // Apply to sub-page only
|
||||
const subConfigId = this.$store.state.currentConfigInfo.confId;
|
||||
const sectionStorageKey = `${localStorageKeys.CONF_SECTIONS}-${subConfigId}`;
|
||||
const pageInfoStorageKey = `${localStorageKeys.PAGE_INFO}-${subConfigId}`;
|
||||
const themeStoreKey = `${localStorageKeys.THEME}-${subConfigId}`;
|
||||
localStorage.setItem(sectionStorageKey, JSON.stringify(config.sections));
|
||||
localStorage.setItem(pageInfoStorageKey, JSON.stringify(config.pageInfo));
|
||||
localStorage.setItem(themeStoreKey, config.appConfig.theme);
|
||||
} else { // Apply to main config
|
||||
localStorage.setItem(localStorageKeys.CONF_SECTIONS, JSON.stringify(config.sections));
|
||||
localStorage.setItem(localStorageKeys.APP_CONFIG, JSON.stringify(config.appConfig));
|
||||
localStorage.setItem(localStorageKeys.PAGE_INFO, JSON.stringify(config.pageInfo));
|
||||
localStorage.setItem(localStorageKeys.CONF_PAGES, JSON.stringify(config.pages || []));
|
||||
if (config.appConfig.theme) {
|
||||
localStorage.setItem(localStorageKeys.THEME, config.appConfig.theme);
|
||||
}
|
||||
}
|
||||
// Save hashed token in local storage
|
||||
this.setBackupIdLocally(backupId, this.restorePassword);
|
||||
|
@ -115,7 +115,9 @@ export default {
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.jsonData = this.config;
|
||||
const jsonData = { ...this.config };
|
||||
jsonData.sections = jsonData.sections.map(({ filteredItems, ...section }) => section);
|
||||
this.jsonData = jsonData;
|
||||
if (!this.allowWriteToDisk) this.saveMode = 'local';
|
||||
},
|
||||
methods: {
|
||||
@ -141,7 +143,11 @@ export default {
|
||||
this.$modal.hide(modalNames.CONF_EDITOR);
|
||||
},
|
||||
writeToDisk() {
|
||||
this.writeConfigToDisk(this.config);
|
||||
const newData = this.jsonData;
|
||||
this.writeConfigToDisk(newData);
|
||||
// this.$store.commit(StoreKeys.SET_APP_CONFIG, newData.appConfig);
|
||||
this.$store.commit(StoreKeys.SET_PAGE_INFO, newData.pageInfo);
|
||||
this.$store.commit(StoreKeys.SET_SECTIONS, newData.sections);
|
||||
},
|
||||
saveLocally() {
|
||||
const msg = this.$t('interactive-editor.menu.save-locally-warning');
|
||||
|
@ -94,6 +94,7 @@ export default {
|
||||
const raw = rawAppConfig;
|
||||
const isEmptyObject = (obj) => (typeof obj === 'object' && Object.keys(obj).length === 0);
|
||||
const isEmpty = (value) => (value === undefined || isEmptyObject(value));
|
||||
|
||||
// Delete empty values
|
||||
Object.keys(raw).forEach(key => {
|
||||
if (isEmpty(raw[key])) delete raw[key];
|
||||
|
@ -9,7 +9,8 @@
|
||||
:target="anchorTarget"
|
||||
:class="`item ${makeClassList}`"
|
||||
v-tooltip="getTooltipOptions()"
|
||||
rel="noopener noreferrer" tabindex="0"
|
||||
:rel="`${item.rel || 'noopener noreferrer'}`"
|
||||
tabindex="0"
|
||||
:id="`link-${item.id}`"
|
||||
:style="customStyle"
|
||||
>
|
||||
@ -258,6 +259,9 @@ export default {
|
||||
overflow: hidden;
|
||||
span.text {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,8 +74,8 @@
|
||||
</div>
|
||||
<!-- Modal for opening in modal view -->
|
||||
<IframeModal
|
||||
:ref="`iframeModal`"
|
||||
:name="`iframeModal`"
|
||||
:ref="`iframeModal-${groupId}`"
|
||||
:name="`iframeModal-${groupId}`"
|
||||
@closed="$emit('itemClicked')"
|
||||
/>
|
||||
<!-- Edit item menu -->
|
||||
@ -213,7 +213,7 @@ export default {
|
||||
methods: {
|
||||
/* Opens the iframe modal */
|
||||
triggerModal(url) {
|
||||
this.$refs.iframeModal.show(url);
|
||||
this.$refs[`iframeModal-${this.groupId}`].show(url);
|
||||
},
|
||||
/* Sorts items alphabetically using the title attribute */
|
||||
sortAlphabetically(items) {
|
||||
|
@ -1,16 +1,13 @@
|
||||
<template>
|
||||
<!-- User Footer -->
|
||||
<footer v-if="text && text !== '' && visible" v-html="text"></footer>
|
||||
<!-- Default Footer -->
|
||||
<footer v-else-if="visible">
|
||||
<span v-if="$store.state.currentConfigInfo" class="path-to-config">
|
||||
Using: {{ $store.state.currentConfigInfo.confPath }}
|
||||
</span>
|
||||
<span>
|
||||
{{ $t('footer.dev-by') }} <a :href="authorUrl">{{authorName}}</a>.
|
||||
{{ $t('footer.licensed-under') }} <a :href="licenseUrl">{{license}}</a>
|
||||
{{ showCopyright? '©': '' }} {{date}}.
|
||||
{{ $t('footer.get-the') }} <a :href="repoUrl">{{ $t('footer.source-code') }}</a>.
|
||||
<footer v-if="visible">
|
||||
<!-- User-defined footer -->
|
||||
<span v-if="text" v-html="text"></span>
|
||||
<!-- Default footer -->
|
||||
<span v-else>
|
||||
<a :href="defaultInfo.projectUrl">Dashy</a> is free & open source
|
||||
- licensed under <a :href="defaultInfo.licenseUrl">{{defaultInfo.license}}</a>,
|
||||
© <a :href="defaultInfo.authorUrl">{{defaultInfo.authorName}}</a> {{defaultInfo.date}}.
|
||||
Get support on GitHub, at <a :href="defaultInfo.repoUrl">{{defaultInfo.repoName}}</a>.
|
||||
</span>
|
||||
</footer>
|
||||
</template>
|
||||
@ -23,13 +20,20 @@ export default {
|
||||
name: 'Footer',
|
||||
props: {
|
||||
text: String,
|
||||
authorName: { type: String, default: 'Alicia Sykes' },
|
||||
authorUrl: { type: String, default: 'https://aliciasykes.com' },
|
||||
license: { type: String, default: 'MIT' },
|
||||
licenseUrl: { type: String, default: 'https://gist.github.com/Lissy93/143d2ee01ccc5c052a17' },
|
||||
date: { type: String, default: `${new Date().getFullYear()}` },
|
||||
showCopyright: { type: Boolean, default: true },
|
||||
repoUrl: { type: String, default: 'https://github.com/lissy93/dashy' },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultInfo: {
|
||||
authorName: 'Alicia Sykes',
|
||||
authorUrl: 'https://as93.net',
|
||||
license: 'MIT',
|
||||
licenseUrl: 'https://gist.github.com/Lissy93/143d2ee01ccc5c052a17',
|
||||
date: `${new Date().getFullYear()}`,
|
||||
repoUrl: 'https://github.com/lissy93/dashy',
|
||||
repoName: 'Lissy93/Dashy',
|
||||
projectUrl: 'https://dashy.to',
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
visible() {
|
||||
@ -56,7 +60,7 @@ footer {
|
||||
display: none;
|
||||
}
|
||||
span.path-to-config {
|
||||
float: right;
|
||||
float: left;
|
||||
font-size: 0.75rem;
|
||||
margin: 0.1rem 0.5rem 0 0;
|
||||
opacity: var(--dimming-factor);
|
||||
|
@ -66,7 +66,7 @@ export default {
|
||||
span.subtitle {
|
||||
color: var(--heading-text-color);
|
||||
font-style: italic;
|
||||
text-shadow: 1px 1px 2px #130f23;
|
||||
text-shadow: 1px 1px 2px #130f2347;
|
||||
opacity: var(--dimming-factor);
|
||||
}
|
||||
img.site-logo {
|
||||
|
@ -95,7 +95,8 @@ export default {
|
||||
},
|
||||
/* If configured, launch specific app when hotkey pressed */
|
||||
handleHotKey(key) {
|
||||
const usersHotKeys = this.getCustomKeyShortcuts();
|
||||
const sections = this.$store.getters.sections || [];
|
||||
const usersHotKeys = this.getCustomKeyShortcuts(sections);
|
||||
usersHotKeys.forEach((hotkey) => {
|
||||
if (hotkey.hotkey === parseInt(key, 10)) {
|
||||
if (hotkey.url) window.open(hotkey.url, '_blank');
|
||||
|
@ -8,7 +8,7 @@
|
||||
:value="$store.getters.theme"
|
||||
class="theme-dropdown"
|
||||
:tabindex="-2"
|
||||
@input="themeChanged"
|
||||
@input="themeChangedInUI"
|
||||
/>
|
||||
</div>
|
||||
<IconPalette
|
||||
@ -28,18 +28,13 @@
|
||||
<script>
|
||||
|
||||
import CustomThemeMaker from '@/components/Settings/CustomThemeMaker';
|
||||
import {
|
||||
LoadExternalTheme,
|
||||
ApplyLocalTheme,
|
||||
ApplyCustomVariables,
|
||||
} from '@/utils/ThemeHelper';
|
||||
import Defaults, { localStorageKeys } from '@/utils/defaults';
|
||||
import Keys from '@/utils/StoreMutations';
|
||||
import ErrorHandler from '@/utils/ErrorHandler';
|
||||
import IconPalette from '@/assets/interface-icons/config-color-palette.svg';
|
||||
import ThemingMixin from '@/mixins/ThemingMixin';
|
||||
|
||||
export default {
|
||||
name: 'ThemeSelector',
|
||||
mixins: [ThemingMixin],
|
||||
props: {
|
||||
hidePallete: Boolean,
|
||||
},
|
||||
@ -47,101 +42,16 @@ export default {
|
||||
CustomThemeMaker,
|
||||
IconPalette,
|
||||
},
|
||||
watch: {
|
||||
/* When theme in VueX store changes, then update theme */
|
||||
themeFromStore(newTheme) {
|
||||
this.selectedTheme = newTheme;
|
||||
this.updateTheme(newTheme);
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedTheme: '',
|
||||
themeConfiguratorOpen: false, // Control the opening of theme config popup
|
||||
themeHelper: new LoadExternalTheme(),
|
||||
ApplyLocalTheme,
|
||||
ApplyCustomVariables,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
/* Get appConfig from store */
|
||||
appConfig() {
|
||||
return this.$store.getters.appConfig;
|
||||
},
|
||||
/* Get users theme from store */
|
||||
themeFromStore() {
|
||||
return this.$store.getters.theme;
|
||||
},
|
||||
/* Combines all theme names (builtin and user defined) together */
|
||||
themeNames: function themeNames() {
|
||||
const externalThemeNames = Object.keys(this.externalThemes);
|
||||
const specialThemes = ['custom'];
|
||||
return [...this.extraThemeNames, ...externalThemeNames,
|
||||
...Defaults.builtInThemes, ...specialThemes];
|
||||
},
|
||||
extraThemeNames() {
|
||||
const userThemes = this.appConfig.cssThemes || [];
|
||||
if (typeof userThemes === 'string') return [userThemes];
|
||||
return userThemes;
|
||||
},
|
||||
/* Returns an array of links to external CSS from the Config */
|
||||
externalThemes() {
|
||||
const availibleThemes = {};
|
||||
if (this.appConfig && this.appConfig.externalStyleSheet) {
|
||||
const externals = this.appConfig.externalStyleSheet;
|
||||
if (Array.isArray(externals)) {
|
||||
externals.forEach((ext, i) => {
|
||||
availibleThemes[`External Stylesheet ${i + 1}`] = ext;
|
||||
});
|
||||
} else if (typeof externals === 'string') {
|
||||
availibleThemes['External Stylesheet'] = this.appConfig.externalStyleSheet;
|
||||
} else {
|
||||
ErrorHandler('External stylesheets must be of type string or string[]');
|
||||
}
|
||||
}
|
||||
// availibleThemes.Default = '#';
|
||||
return availibleThemes;
|
||||
},
|
||||
},
|
||||
computed: {},
|
||||
mounted() {
|
||||
const initialTheme = this.getInitialTheme();
|
||||
this.selectedTheme = initialTheme;
|
||||
// Quicker loading, if the theme is local we can apply it immidiatley
|
||||
if (this.isThemeLocal(initialTheme)) {
|
||||
this.updateTheme(initialTheme);
|
||||
}
|
||||
|
||||
// If it's an external stylesheet, then wait for promise to resolve
|
||||
if (this.externalThemes && Object.entries(this.externalThemes).length > 0) {
|
||||
const added = Object.keys(this.externalThemes).map(
|
||||
name => this.themeHelper.add(name, this.externalThemes[name]),
|
||||
);
|
||||
// Once, added, then apply users initial theme
|
||||
Promise.all(added).then(() => {
|
||||
this.updateTheme(initialTheme);
|
||||
});
|
||||
}
|
||||
this.initializeTheme();
|
||||
},
|
||||
methods: {
|
||||
/* Called when dropdown changed
|
||||
* Updates store, which will in turn update theme through watcher
|
||||
*/
|
||||
themeChanged() {
|
||||
const pageId = this.$store.state.currentConfigInfo?.pageId || null;
|
||||
this.$store.commit(Keys.SET_THEME, { theme: this.selectedTheme, pageId });
|
||||
this.updateTheme(this.selectedTheme);
|
||||
},
|
||||
/* Returns the initial theme */
|
||||
getInitialTheme() {
|
||||
const localTheme = localStorage[localStorageKeys.THEME];
|
||||
if (localTheme && localTheme !== 'undefined') return localTheme;
|
||||
return this.appConfig.theme || Defaults.theme;
|
||||
},
|
||||
/* Determines if a given theme is local / not a custom user stylesheet */
|
||||
isThemeLocal(themeToCheck) {
|
||||
const localThemes = [...Defaults.builtInThemes, ...this.extraThemeNames];
|
||||
return localThemes.includes(themeToCheck);
|
||||
},
|
||||
/* Opens the theme color configurator popup */
|
||||
openThemeConfigurator() {
|
||||
this.$store.commit(Keys.SET_MODAL_OPEN, true);
|
||||
@ -154,24 +64,6 @@ export default {
|
||||
this.themeConfiguratorOpen = false;
|
||||
}
|
||||
},
|
||||
/* Updates theme. Checks if the new theme is local or external,
|
||||
and calls appropirate updating function. Updates local storage */
|
||||
updateTheme(newTheme) {
|
||||
if (newTheme === 'Default') {
|
||||
this.resetToDefault();
|
||||
this.themeHelper.theme = 'Default';
|
||||
} else if (this.isThemeLocal(newTheme)) {
|
||||
this.ApplyLocalTheme(newTheme);
|
||||
} else {
|
||||
this.themeHelper.theme = newTheme;
|
||||
}
|
||||
this.ApplyCustomVariables(newTheme);
|
||||
// localStorage.setItem(localStorageKeys.THEME, newTheme);
|
||||
},
|
||||
/* Removes any applied themes */
|
||||
resetToDefault() {
|
||||
document.getElementsByTagName('html')[0].removeAttribute('data-theme');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -74,6 +74,7 @@ export default {
|
||||
this.jokeLine2 = data.delivery;
|
||||
} else if (this.jokeType === 'single') {
|
||||
this.jokeLine1 = data.joke;
|
||||
this.jokeLine2 = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -94,7 +94,7 @@ export default {
|
||||
}
|
||||
},
|
||||
processData(data) {
|
||||
this.data = data.data.sort((a, b) => a.vmid > b.vmid);
|
||||
this.data = data.data.sort((a, b) => Number(a.vmid) > Number(b.vmid));
|
||||
if (this.hideTemplates) {
|
||||
this.data = this.data.filter(item => item.template !== 1);
|
||||
}
|
||||
|
242
src/components/Widgets/UptimeKuma.vue
Normal file
242
src/components/Widgets/UptimeKuma.vue
Normal file
@ -0,0 +1,242 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="monitors">
|
||||
<div v-for="(monitor, index) in monitors" :key="index" class="item-wrapper">
|
||||
<div class="item monitor-row">
|
||||
<div class="title-title"><span class="text">{{ monitor.name }}</span></div>
|
||||
<div class="monitors-container">
|
||||
<div class="status-container">
|
||||
<span class="status-pill" :class="[monitor.statusClass]">{{ monitor.status }}</span>
|
||||
</div>
|
||||
<div class="status-container">
|
||||
<span class="response-time">{{ monitor.responseTime }}ms</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-if="errorMessage">
|
||||
<div class="error-message">
|
||||
<span class="text">{{ errorMessage }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* A simple example which you can use as a template for creating your own widget.
|
||||
* Takes two optional parameters (`text` and `count`), and fetches a list of images
|
||||
* from dummyapis.com, then renders the results to the UI.
|
||||
*/
|
||||
import WidgetMixin from '@/mixins/WidgetMixin';
|
||||
|
||||
export default {
|
||||
mixins: [WidgetMixin],
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
monitors: null,
|
||||
errorMessage: null,
|
||||
errorMessageConstants: {
|
||||
missingApiKey: 'No API key set',
|
||||
missingUrl: 'No URL set',
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.fetchData();
|
||||
},
|
||||
computed: {
|
||||
/* Get API key for access to instance */
|
||||
apiKey() {
|
||||
const { apiKey } = this.options;
|
||||
|
||||
return apiKey;
|
||||
},
|
||||
/* Get instance URL */
|
||||
url() {
|
||||
const { url } = this.options;
|
||||
|
||||
return url;
|
||||
},
|
||||
/* Create authorisation header for the instance from the apiKey */
|
||||
authHeaders() {
|
||||
if (!this.options.apiKey) {
|
||||
return {};
|
||||
}
|
||||
const encoded = window.btoa(`:${this.options.apiKey}`);
|
||||
return { Authorization: `Basic ${encoded}` };
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/* The update() method extends mixin, used to update the data.
|
||||
* It's called by parent component, when the user presses update
|
||||
*/
|
||||
update() {
|
||||
this.startLoading();
|
||||
this.fetchData();
|
||||
},
|
||||
/* Make the data request to the computed API endpoint */
|
||||
fetchData() {
|
||||
const { authHeaders, url } = this;
|
||||
|
||||
if (!this.optionsValid({ authHeaders, url })) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.makeRequest(url, authHeaders)
|
||||
.then(this.processData);
|
||||
},
|
||||
/* Convert API response data into a format to be consumed by the UI */
|
||||
processData(response) {
|
||||
const monitorRows = this.getMonitorRows(response);
|
||||
|
||||
const monitors = new Map();
|
||||
|
||||
for (let index = 0; index < monitorRows.length; index += 1) {
|
||||
const row = monitorRows[index];
|
||||
this.processRow(row, monitors);
|
||||
}
|
||||
|
||||
this.monitors = Array.from(monitors.values());
|
||||
},
|
||||
getMonitorRows(response) {
|
||||
return response.split('\n').filter(row => row.startsWith('monitor_'));
|
||||
},
|
||||
processRow(row, monitors) {
|
||||
const dataType = this.getRowDataType(row);
|
||||
const monitorName = this.getRowMonitorName(row);
|
||||
|
||||
if (!monitors.has(monitorName)) {
|
||||
monitors.set(monitorName, { name: monitorName });
|
||||
}
|
||||
|
||||
const monitor = monitors.get(monitorName);
|
||||
const value = this.getRowValue(row);
|
||||
|
||||
const updated = this.setMonitorValue(dataType, monitor, value);
|
||||
|
||||
monitors.set(monitorName, updated);
|
||||
},
|
||||
setMonitorValue(key, monitor, value) {
|
||||
const copy = { ...monitor };
|
||||
switch (key) {
|
||||
case 'monitor_cert_days_remaining': {
|
||||
copy.certDaysRemaining = value;
|
||||
break;
|
||||
}
|
||||
case 'monitor_cert_is_valid': {
|
||||
copy.certValid = value;
|
||||
break;
|
||||
}
|
||||
case 'monitor_response_time': {
|
||||
copy.responseTime = value;
|
||||
break;
|
||||
}
|
||||
case 'monitor_status': {
|
||||
copy.status = value === '1' ? 'Up' : 'Down';
|
||||
copy.statusClass = copy.status.toLowerCase();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return copy;
|
||||
},
|
||||
getRowValue(row) {
|
||||
return this.getValueWithRegex(row, /\b\d+\b$/);
|
||||
},
|
||||
getRowMonitorName(row) {
|
||||
return this.getValueWithRegex(row, /monitor_name="([^"]+)"/);
|
||||
},
|
||||
getRowDataType(row) {
|
||||
return this.getValueWithRegex(row, /^(.*?)\{/);
|
||||
},
|
||||
getValueWithRegex(string, regex) {
|
||||
const result = string.match(regex);
|
||||
|
||||
const isArray = Array.isArray(result);
|
||||
|
||||
if (!isArray) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return result.length > 1 ? result[1] : result[0];
|
||||
},
|
||||
optionsValid({ url, authHeaders }) {
|
||||
const errors = [];
|
||||
if (url === undefined) {
|
||||
errors.push(this.errorMessageConstants.missingUrl);
|
||||
}
|
||||
|
||||
if (authHeaders === undefined) {
|
||||
errors.push(this.errorMessageConstants.missingApiKey);
|
||||
}
|
||||
|
||||
if (errors.length === 0) { return true; }
|
||||
|
||||
this.errorMessage = errors.join('\n');
|
||||
return false;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.status-pill {
|
||||
border-radius: 50em;
|
||||
box-sizing: border-box;
|
||||
font-size: 0.75em;
|
||||
display: inline-block;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: baseline;
|
||||
padding: .35em .65em;
|
||||
margin: 1em 0.5em;
|
||||
min-width: 64px;
|
||||
|
||||
&.up {
|
||||
background-color: rgb(92, 221, 139);
|
||||
color: black;
|
||||
}
|
||||
|
||||
&.down {
|
||||
background-color: rgb(220, 53, 69);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
div.item.monitor-row:hover {
|
||||
background-color: var(--item-background);
|
||||
color: var(--current-color);
|
||||
opacity: 1;
|
||||
|
||||
div.title-title>span.text {
|
||||
color: var(--current-color);
|
||||
}
|
||||
}
|
||||
|
||||
.monitors-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.monitor-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.35em 0.5em;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.title-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
@ -115,6 +115,7 @@ const COMPAT = {
|
||||
'synology-download': 'SynologyDownload',
|
||||
'system-info': 'SystemInfo',
|
||||
'tfl-status': 'TflStatus',
|
||||
'uptime-kuma': 'UptimeKuma',
|
||||
'wallet-balance': 'WalletBalance',
|
||||
weather: 'Weather',
|
||||
'weather-forecast': 'WeatherForecast',
|
||||
@ -205,14 +206,16 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/styles/media-queries.scss';
|
||||
@import "@/styles/media-queries.scss";
|
||||
|
||||
.widget-base {
|
||||
position: relative;
|
||||
padding: 0.75rem 0.5rem 0.5rem 0.5rem;
|
||||
background: var(--widget-base-background);
|
||||
box-shadow: var(--widget-base-shadow, none);
|
||||
|
||||
// Refresh and full-page action buttons
|
||||
button.action-btn {
|
||||
button.action-btn {
|
||||
height: 1rem;
|
||||
min-width: auto;
|
||||
width: 1.75rem;
|
||||
@ -223,21 +226,26 @@ export default {
|
||||
border: none;
|
||||
opacity: var(--dimming-factor);
|
||||
color: var(--widget-text-color);
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
color: var(--widget-background-color);
|
||||
}
|
||||
|
||||
&.update-btn {
|
||||
right: -0.25rem;
|
||||
}
|
||||
|
||||
&.open-btn {
|
||||
right: 1.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Optional widget label
|
||||
.widget-label {
|
||||
color: var(--widget-text-color);
|
||||
}
|
||||
|
||||
// Actual widget container
|
||||
.widget-wrap {
|
||||
&.has-error {
|
||||
@ -245,9 +253,11 @@ export default {
|
||||
opacity: 0.5;
|
||||
border-radius: var(--curve-factor);
|
||||
background: #ffff0040;
|
||||
|
||||
&:hover { background: none; }
|
||||
}
|
||||
}
|
||||
|
||||
// Error message output
|
||||
.widget-error {
|
||||
p.error-msg {
|
||||
@ -256,12 +266,14 @@ export default {
|
||||
font-size: 1rem;
|
||||
margin: 0 auto 0.5rem auto;
|
||||
}
|
||||
|
||||
p.error-output {
|
||||
font-family: var(--font-monospace);
|
||||
color: var(--widget-text-color);
|
||||
font-size: 0.85rem;
|
||||
margin: 0.5rem auto;
|
||||
}
|
||||
|
||||
p.retry-link {
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
@ -270,14 +282,17 @@ export default {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Loading spinner
|
||||
.loading {
|
||||
margin: 0.2rem auto;
|
||||
text-align: center;
|
||||
|
||||
svg.loader {
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
// Hide widget contents while loading
|
||||
&.is-loading {
|
||||
.widget-wrap {
|
||||
@ -285,5 +300,4 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
@ -2,7 +2,7 @@
|
||||
* A Vue directive to trigger an event when the user
|
||||
* clicks anywhere other than the specified elements
|
||||
* Used to close context menus popup modals and tips
|
||||
* Dashy: Licensed under MIT - (C) Alicia Sykes 2022
|
||||
* Dashy: Licensed under MIT - (C) Alicia Sykes 2024
|
||||
*/
|
||||
|
||||
const instances = []; // List of click event instances
|
||||
|
@ -2,7 +2,7 @@
|
||||
* A Vue directive to call event when element is long-pressed
|
||||
* Used to open context menus on touch-enabled devices
|
||||
* Inspired by: FeliciousX/vue-directive-long-press
|
||||
* Dashy: Licensed under MIT - (C) Alicia Sykes 2022
|
||||
* Dashy: Licensed under MIT - (C) Alicia Sykes 2024
|
||||
*/
|
||||
|
||||
const LONG_PRESS_DEFAULT_DELAY = 750;
|
||||
|
26
src/main.js
26
src/main.js
@ -13,14 +13,16 @@ import TreeView from 'vue-json-tree-view';
|
||||
|
||||
// Import base Dashy components and utils
|
||||
import Dashy from '@/App.vue'; // Main Dashy Vue app
|
||||
import router from '@/router'; // Router, for navigation
|
||||
import store from '@/store'; // Store, for local state management
|
||||
import router from '@/router'; // Router, for navigation
|
||||
import serviceWorker from '@/utils/InitServiceWorker'; // Service worker initialization
|
||||
import { messages } from '@/utils/languages'; // Language texts
|
||||
import ErrorReporting from '@/utils/ErrorReporting'; // Error reporting initializer (off)
|
||||
import clickOutside from '@/directives/ClickOutside'; // Directive for closing popups, modals, etc
|
||||
import { toastedOptions, tooltipOptions, language as defaultLanguage } from '@/utils/defaults';
|
||||
import { initKeycloakAuth, isKeycloakEnabled } from '@/utils/KeycloakAuth';
|
||||
import { initHeaderAuth, isHeaderAuthEnabled } from '@/utils/HeaderAuth';
|
||||
import Keys from '@/utils/StoreMutations';
|
||||
|
||||
// Initialize global Vue components
|
||||
Vue.use(VueI18n);
|
||||
@ -58,11 +60,17 @@ const mount = () => new Vue({
|
||||
store, router, render, i18n,
|
||||
}).$mount('#app');
|
||||
|
||||
// If Keycloak not enabled, then proceed straight to the app
|
||||
if (!isKeycloakEnabled()) {
|
||||
mount();
|
||||
} else { // Keycloak is enabled, redirect to KC login page
|
||||
initKeycloakAuth()
|
||||
.then(() => mount())
|
||||
.catch(() => window.location.reload());
|
||||
}
|
||||
store.dispatch(Keys.INITIALIZE_CONFIG).then(() => {
|
||||
// Keycloak is enabled, redirect to KC login page
|
||||
if (isKeycloakEnabled()) {
|
||||
initKeycloakAuth()
|
||||
.then(() => mount())
|
||||
.catch(() => window.location.reload());
|
||||
} else if (isHeaderAuthEnabled()) {
|
||||
initHeaderAuth()
|
||||
.then(() => mount())
|
||||
.catch(() => window.location.reload());
|
||||
} else { // If Keycloak not enabled, then proceed straight to the app
|
||||
mount();
|
||||
}
|
||||
});
|
||||
|
@ -21,12 +21,22 @@ export default {
|
||||
return;
|
||||
}
|
||||
// 1. Get the config, and strip appConfig if is sub-page
|
||||
const isSubPag = !!this.$store.state.currentConfigInfo;
|
||||
const isSubPag = !!this.$store.state.currentConfigInfo.confId;
|
||||
const jsonConfig = config;
|
||||
if (isSubPag) delete jsonConfig.appConfig;
|
||||
jsonConfig.sections = jsonConfig.sections.map(({ filteredItems, ...section }) => section);
|
||||
// If a sub-config, then remove appConfig, and check path isn't an external URL
|
||||
if (isSubPag) {
|
||||
delete jsonConfig.appConfig;
|
||||
if (this.$store.state.currentConfigInfo.confPath.includes('http')) {
|
||||
ErrorHandler('Cannot save to an external URL');
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 2. Convert JSON into YAML
|
||||
const yamlOptions = {};
|
||||
const yaml = jsYaml.dump(jsonConfig, yamlOptions);
|
||||
const strjsonConfig = JSON.stringify(jsonConfig);
|
||||
const jsonObj = JSON.parse(strjsonConfig);
|
||||
const yaml = jsYaml.dump(jsonObj, yamlOptions);
|
||||
// 3. Prepare the request
|
||||
const baseUrl = process.env.VUE_APP_DOMAIN || window.location.origin;
|
||||
const endpoint = `${baseUrl}${serviceEndpoints.save}`;
|
||||
@ -63,20 +73,39 @@ export default {
|
||||
ErrorHandler('Unable to save changes locally, this feature has been disabled');
|
||||
return;
|
||||
}
|
||||
localStorage.setItem(localStorageKeys.CONF_SECTIONS, JSON.stringify(config.sections));
|
||||
localStorage.setItem(localStorageKeys.PAGE_INFO, JSON.stringify(config.pageInfo));
|
||||
localStorage.setItem(localStorageKeys.APP_CONFIG, JSON.stringify(config.appConfig));
|
||||
|
||||
const isSubPag = !!this.$store.state.currentConfigInfo.confId;
|
||||
if (isSubPag) { // Save for sub-page only
|
||||
const configId = this.$store.state.currentConfigInfo.confId;
|
||||
const localStorageKeySections = `${localStorageKeys.CONF_SECTIONS}-${configId}`;
|
||||
const localStorageKeyPageInfo = `${localStorageKeys.PAGE_INFO}-${configId}`;
|
||||
localStorage.setItem(localStorageKeySections, JSON.stringify(config.sections));
|
||||
localStorage.setItem(localStorageKeyPageInfo, JSON.stringify(config.pageInfo));
|
||||
} else { // Or save to main config
|
||||
localStorage.setItem(localStorageKeys.CONF_SECTIONS, JSON.stringify(config.sections));
|
||||
localStorage.setItem(localStorageKeys.PAGE_INFO, JSON.stringify(config.pageInfo));
|
||||
localStorage.setItem(localStorageKeys.APP_CONFIG, JSON.stringify(config.appConfig));
|
||||
}
|
||||
|
||||
if (config.appConfig.theme) {
|
||||
localStorage.setItem(localStorageKeys.THEME, config.appConfig.theme);
|
||||
}
|
||||
InfoHandler('Config has succesfully been saved in browser storage', 'Config Update');
|
||||
InfoHandler('Config has successfully been saved in browser storage', 'Config Update');
|
||||
this.showToast(this.$t('config-editor.success-msg-local'), true);
|
||||
this.$store.commit(StoreKeys.SET_EDIT_MODE, false);
|
||||
},
|
||||
carefullyClearLocalStorage() {
|
||||
// Delete the main keys
|
||||
localStorage.removeItem(localStorageKeys.PAGE_INFO);
|
||||
localStorage.removeItem(localStorageKeys.APP_CONFIG);
|
||||
localStorage.removeItem(localStorageKeys.CONF_SECTIONS);
|
||||
// Then, if we've got any sub-pages, delete those too
|
||||
(this.$store.getters.pages || []).forEach((page) => {
|
||||
const localStorageKeySections = `${localStorageKeys.CONF_SECTIONS}-${page.id}`;
|
||||
const localStorageKeyPageInfo = `${localStorageKeys.PAGE_INFO}-${page.id}`;
|
||||
localStorage.removeItem(localStorageKeySections);
|
||||
localStorage.removeItem(localStorageKeyPageInfo);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -6,7 +6,6 @@ import Defaults, { localStorageKeys, iconCdns } from '@/utils/defaults';
|
||||
import Keys from '@/utils/StoreMutations';
|
||||
import { searchTiles } from '@/utils/Search';
|
||||
import { checkItemVisibility } from '@/utils/CheckItemVisibility';
|
||||
import { GetTheme, ApplyLocalTheme, ApplyCustomVariables } from '@/utils/ThemeHelper';
|
||||
|
||||
const HomeMixin = {
|
||||
props: {
|
||||
@ -36,22 +35,28 @@ const HomeMixin = {
|
||||
searchValue: '',
|
||||
}),
|
||||
async mounted() {
|
||||
await this.getConfigForRoute();
|
||||
// await this.getConfigForRoute();
|
||||
},
|
||||
watch: {
|
||||
async $route() {
|
||||
await this.getConfigForRoute();
|
||||
this.setTheme();
|
||||
this.loadUpConfig();
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
this.loadUpConfig();
|
||||
},
|
||||
methods: {
|
||||
async getConfigForRoute() {
|
||||
this.$store.commit(Keys.SET_CURRENT_SUB_PAGE, this.subPageInfo);
|
||||
if (this.subPageInfo && this.subPageInfo.confPath) { // Get config for sub-page
|
||||
await this.$store.dispatch(Keys.INITIALIZE_MULTI_PAGE_CONFIG, this.subPageInfo.confPath);
|
||||
} else { // Otherwise, use main config
|
||||
this.$store.commit(Keys.USE_MAIN_CONFIG);
|
||||
}
|
||||
/* When page loaded / sub-page changed, initiate config fetch */
|
||||
async loadUpConfig() {
|
||||
const subPage = this.determineConfigFile();
|
||||
await this.$store.dispatch(Keys.INITIALIZE_CONFIG, subPage);
|
||||
},
|
||||
/* Based on the current route, get which config to display, null will use default */
|
||||
determineConfigFile() {
|
||||
const pagePath = this.$router.currentRoute.path;
|
||||
const isSubPage = new RegExp((/(home|workspace|minimal)\/[a-zA-Z0-9-]+/g)).test(pagePath);
|
||||
const subPageName = isSubPage ? pagePath.split('/').pop() : null;
|
||||
return subPageName;
|
||||
},
|
||||
/* TEMPORARY: If on sub-page, check if custom theme is set and return it */
|
||||
getSubPageTheme() {
|
||||
@ -63,9 +68,9 @@ const HomeMixin = {
|
||||
}
|
||||
},
|
||||
setTheme() {
|
||||
const theme = this.getSubPageTheme() || GetTheme();
|
||||
ApplyLocalTheme(theme);
|
||||
ApplyCustomVariables(theme);
|
||||
// const theme = this.getSubPageTheme() || GetTheme();
|
||||
// ApplyLocalTheme(theme);
|
||||
// ApplyCustomVariables(theme);
|
||||
},
|
||||
updateModalVisibility(modalState) {
|
||||
this.$store.commit('SET_MODAL_OPEN', modalState);
|
||||
|
143
src/mixins/ThemingMixin.js
Normal file
143
src/mixins/ThemingMixin.js
Normal file
@ -0,0 +1,143 @@
|
||||
/**
|
||||
* This mixin can be extended by any component or view which needs to manage themes
|
||||
* It handles fetching and applying themes from the store, updating themes,
|
||||
* applying custom CSS variables and loading external stylesheets.
|
||||
* */
|
||||
|
||||
import Keys from '@/utils/StoreMutations';
|
||||
import ErrorHandler from '@/utils/ErrorHandler';
|
||||
import { builtInThemes, localStorageKeys, mainCssVars } from '@/utils/defaults';
|
||||
|
||||
const ThemingMixin = {
|
||||
data: () => ({
|
||||
selectedTheme: '', // Used only to bind current them to theme dropdown
|
||||
}),
|
||||
computed: {
|
||||
/* This is the theme from the central store. When it changes, the UI will update */
|
||||
themeFromStore() {
|
||||
return this.$store.getters.theme;
|
||||
},
|
||||
appConfig() {
|
||||
return this.$store.getters.appConfig;
|
||||
},
|
||||
/* Any extra user-defined themes, to add to dropdown */
|
||||
extraThemeNames() {
|
||||
const userThemes = this.appConfig?.cssThemes || [];
|
||||
if (typeof userThemes === 'string') return [userThemes];
|
||||
return userThemes;
|
||||
},
|
||||
/* If user specified external stylesheet(s), format and return */
|
||||
externalThemes() {
|
||||
const availableThemes = {};
|
||||
if (this.appConfig?.externalStyleSheet) {
|
||||
const externals = this.appConfig.externalStyleSheet;
|
||||
if (Array.isArray(externals)) {
|
||||
externals.forEach((ext, i) => {
|
||||
availableThemes[`External Stylesheet ${i + 1}`] = ext;
|
||||
});
|
||||
} else if (typeof externals === 'string') {
|
||||
availableThemes['External Stylesheet'] = this.appConfig.externalStyleSheet;
|
||||
} else {
|
||||
ErrorHandler('External stylesheets must be of type string or string[]');
|
||||
}
|
||||
}
|
||||
return availableThemes;
|
||||
},
|
||||
/* Combines all theme names for dropdown (built-in, user-defined and stylesheets) */
|
||||
themeNames() {
|
||||
const externalThemeNames = Object.keys(this.externalThemes);
|
||||
return [...this.extraThemeNames, ...externalThemeNames, ...builtInThemes];
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
/* When theme in VueX store changes, then update theme */
|
||||
themeFromStore(newTheme) {
|
||||
if (newTheme) {
|
||||
this.resetToDefault();
|
||||
this.selectedTheme = newTheme;
|
||||
this.updateTheme(newTheme);
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/* Called when user changes theme through the UI
|
||||
* Updates store, which will in turn update theme through watcher
|
||||
*/
|
||||
themeChangedInUI() {
|
||||
this.$store.commit(Keys.SET_THEME, this.selectedTheme); // Update store
|
||||
this.updateTheme(this.selectedTheme); // Apply theme to UI
|
||||
},
|
||||
/**
|
||||
* Gets any custom styles the user has applied, wither from local storage, or from the config
|
||||
* @returns {object} An array of objects, one for each theme, containing kvps for variables
|
||||
*/
|
||||
getCustomColors() {
|
||||
const localColors = JSON.parse(localStorage[localStorageKeys.CUSTOM_COLORS] || '{}');
|
||||
const configColors = this.appConfig.customColors || {};
|
||||
return Object.assign(configColors, localColors);
|
||||
},
|
||||
/* Gets user custom color preferences for current theme, and applies to DOM */
|
||||
applyCustomVariables(theme) {
|
||||
mainCssVars.forEach((vName) => { document.documentElement.style.removeProperty(`--${vName}`); });
|
||||
const themeColors = this.getCustomColors()[theme];
|
||||
if (themeColors) {
|
||||
Object.keys(themeColors).forEach((customVar) => {
|
||||
document.documentElement.style.setProperty(`--${customVar}`, themeColors[customVar]);
|
||||
});
|
||||
}
|
||||
},
|
||||
/* Sets the theme, by updating data-theme attribute on the html tag */
|
||||
applyLocalTheme(newTheme) {
|
||||
const htmlTag = document.getElementsByTagName('html')[0];
|
||||
if (htmlTag.hasAttribute('data-theme')) htmlTag.removeAttribute('data-theme');
|
||||
htmlTag.setAttribute('data-theme', newTheme);
|
||||
},
|
||||
/* If using an external stylesheet, load it in */
|
||||
applyRemoteTheme(href) {
|
||||
this.resetToDefault();
|
||||
const element = document.createElement('link');
|
||||
element.setAttribute('rel', 'stylesheet');
|
||||
element.setAttribute('type', 'text/css');
|
||||
element.setAttribute('id', 'user-defined-stylesheet');
|
||||
element.setAttribute('href', href);
|
||||
document.getElementsByTagName('head')[0].appendChild(element);
|
||||
},
|
||||
/* Determines if a given theme is local / not a custom user stylesheet */
|
||||
isThemeLocal(themeToCheck) {
|
||||
const localThemes = [...builtInThemes, ...this.extraThemeNames];
|
||||
return localThemes.includes(themeToCheck);
|
||||
},
|
||||
/* Updates theme. Checks if the new theme is local or external,
|
||||
and calls appropriate updating function. Updates local storage */
|
||||
updateTheme(newTheme) {
|
||||
if (newTheme.toLowerCase() === 'default') {
|
||||
this.resetToDefault();
|
||||
} else if (this.isThemeLocal(newTheme)) {
|
||||
this.applyLocalTheme(newTheme);
|
||||
} else if (this.externalThemes[newTheme]) {
|
||||
this.applyRemoteTheme(this.externalThemes[newTheme]);
|
||||
}
|
||||
this.applyCustomVariables(newTheme);
|
||||
},
|
||||
/* Removes any applied themes, and deletes any externally loaded stylesheets */
|
||||
resetToDefault() {
|
||||
const externalStyles = document.getElementById('user-defined-stylesheet');
|
||||
if (externalStyles) document.getElementsByTagName('head')[0].removeChild(externalStyles);
|
||||
document.getElementsByTagName('html')[0].removeAttribute('data-theme');
|
||||
},
|
||||
/* Call within mounted hook within a page to apply the correct theme */
|
||||
initializeTheme() {
|
||||
const initialTheme = this.themeFromStore;
|
||||
this.selectedTheme = initialTheme;
|
||||
const hasExternal = this.externalThemes && Object.entries(this.externalThemes).length > 0;
|
||||
|
||||
if (this.isThemeLocal(initialTheme)) {
|
||||
this.updateTheme(initialTheme);
|
||||
} else if (hasExternal) {
|
||||
this.applyRemoteTheme(this.externalThemes[initialTheme]);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default ThemingMixin;
|
@ -14,23 +14,9 @@ import Home from '@/views/Home.vue';
|
||||
|
||||
// Import helper functions, config data and defaults
|
||||
import { isAuthEnabled, isLoggedIn, isGuestAccessEnabled } from '@/utils/Auth';
|
||||
import { makePageSlug, makePageName } from '@/utils/ConfigHelpers';
|
||||
import { metaTagData, startingView, routePaths } from '@/utils/defaults';
|
||||
import { metaTagData, startingView as defaultStartingView, routePaths } from '@/utils/defaults';
|
||||
import ErrorHandler from '@/utils/ErrorHandler';
|
||||
|
||||
// Import data from users conf file. Note that rebuild is required for this to update.
|
||||
import conf from '../public/conf.yml';
|
||||
|
||||
if (!conf) {
|
||||
ErrorHandler('You\'ve not got any data in your config file yet.');
|
||||
}
|
||||
|
||||
// Assign top-level config fields, check not null
|
||||
const config = conf || {};
|
||||
const pages = config.pages || [];
|
||||
const pageInfo = config.pageInfo || {};
|
||||
const appConfig = config.appConfig || {};
|
||||
|
||||
Vue.use(Router);
|
||||
const progress = new Progress({ color: 'var(--progress-bar)' });
|
||||
|
||||
@ -42,16 +28,15 @@ const isAuthenticated = () => {
|
||||
return (!authEnabled || userLoggedIn || guestEnabled);
|
||||
};
|
||||
|
||||
/* Get the users chosen starting view from app config, or return default */
|
||||
const getStartingView = () => appConfig.startingView || startingView;
|
||||
// Get the default starting view from environmental variable
|
||||
const startingView = process.env.VUE_APP_STARTING_VIEW || defaultStartingView;
|
||||
|
||||
/**
|
||||
* Returns the component that should be rendered at the base path,
|
||||
* Defaults to Home, but the user can change this to Workspace of Minimal
|
||||
*/
|
||||
const getStartingComponent = () => {
|
||||
const usersPreference = getStartingView();
|
||||
switch (usersPreference) {
|
||||
switch (startingView) {
|
||||
case 'minimal': return () => import('./views/Minimal.vue');
|
||||
case 'workspace': return () => import('./views/Workspace.vue');
|
||||
default: return Home;
|
||||
@ -59,71 +44,23 @@ const getStartingComponent = () => {
|
||||
};
|
||||
|
||||
/* Returns the meta tags for each route */
|
||||
const makeMetaTags = (defaultTitle) => ({
|
||||
title: pageInfo.title || defaultTitle,
|
||||
metaTags: metaTagData,
|
||||
});
|
||||
|
||||
const makeSubConfigPath = (rawPath) => {
|
||||
if (!rawPath) return '';
|
||||
if (rawPath.startsWith('/') || rawPath.startsWith('http')) return rawPath;
|
||||
else return `/${rawPath}`;
|
||||
};
|
||||
|
||||
/* For each additional config file, create routes for home, minimal and workspace views */
|
||||
const makeMultiPageRoutes = (userPages) => {
|
||||
// If no multi pages specified, or is not array, then return nothing
|
||||
if (!userPages || !Array.isArray(userPages)) return [];
|
||||
const multiPageRoutes = [];
|
||||
// For each user page, create an additional route
|
||||
userPages.forEach((page) => {
|
||||
if (!page.name || !page.path) { // Sumin not right, show warning
|
||||
ErrorHandler('Additional pages must have both a `name` and `path`');
|
||||
}
|
||||
// Props to be passed to home mixin
|
||||
const subPageInfo = {
|
||||
subPageInfo: {
|
||||
confPath: makeSubConfigPath(page.path),
|
||||
pageId: makePageName(page.name),
|
||||
pageTitle: page.name,
|
||||
},
|
||||
};
|
||||
// Create route for default homepage
|
||||
multiPageRoutes.push({
|
||||
path: makePageSlug(page.name, 'home'),
|
||||
name: `${subPageInfo.subPageInfo.pageId}-home`,
|
||||
component: Home,
|
||||
props: subPageInfo,
|
||||
});
|
||||
// Create route for the workspace view
|
||||
multiPageRoutes.push({
|
||||
path: makePageSlug(page.name, 'workspace'),
|
||||
name: `${subPageInfo.subPageInfo.pageId}-workspace`,
|
||||
component: () => import('./views/Workspace.vue'),
|
||||
props: subPageInfo,
|
||||
});
|
||||
// Create route for the minimal view
|
||||
multiPageRoutes.push({
|
||||
path: makePageSlug(page.name, 'minimal'),
|
||||
name: `${subPageInfo.subPageInfo.pageId}-minimal`,
|
||||
component: () => import('./views/Minimal.vue'),
|
||||
props: subPageInfo,
|
||||
});
|
||||
});
|
||||
return multiPageRoutes;
|
||||
const makeMetaTags = (defaultTitle) => {
|
||||
const userTitle = process.env.VUE_APP_TITLE || '';
|
||||
const title = userTitle ? `${userTitle} | ${defaultTitle}` : defaultTitle;
|
||||
return { title, metaTags: metaTagData };
|
||||
};
|
||||
|
||||
/* Routing mode, can be either 'hash', 'history' or 'abstract' */
|
||||
const mode = appConfig.routingMode || 'history';
|
||||
const mode = process.env.VUE_APP_ROUTING_MODE || 'history';
|
||||
|
||||
/* List of all routes, props, components and metadata */
|
||||
const router = new Router({
|
||||
mode,
|
||||
routes: [
|
||||
...makeMultiPageRoutes(pages),
|
||||
// ...makeMultiPageRoutes(pages),
|
||||
{ // The default view can be customized by the user
|
||||
path: '/',
|
||||
name: `landing-page-${getStartingView()}`,
|
||||
name: `landing-page-${startingView}`,
|
||||
component: getStartingComponent(),
|
||||
meta: makeMetaTags('Home Page'),
|
||||
},
|
||||
@ -197,7 +134,7 @@ const router = new Router({
|
||||
* if so, then ensure that they are correctly logged in as a valid user
|
||||
* If not logged in, prevent all access and redirect them to login page
|
||||
* */
|
||||
router.beforeEach((to, from, next) => {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
progress.start();
|
||||
if (to.name !== 'login' && !isAuthenticated()) next({ name: 'login' });
|
||||
else next();
|
||||
|
162
src/store.js
162
src/store.js
@ -4,22 +4,21 @@ 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';
|
||||
import { makePageName, formatConfigPath, componentVisibility } from '@/utils/ConfigHelpers';
|
||||
import { applyItemId } from '@/utils/SectionHelpers';
|
||||
import filterUserSections from '@/utils/CheckSectionVisibility';
|
||||
import ErrorHandler, { InfoHandler, InfoKeys } from '@/utils/ErrorHandler';
|
||||
import { isUserAdmin } from '@/utils/Auth';
|
||||
import { localStorageKeys } from './utils/defaults';
|
||||
import { localStorageKeys, theme as defaultTheme } from './utils/defaults';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
const {
|
||||
INITIALIZE_CONFIG,
|
||||
INITIALIZE_MULTI_PAGE_CONFIG,
|
||||
INITIALIZE_ROOT_CONFIG,
|
||||
SET_CONFIG,
|
||||
SET_REMOTE_CONFIG,
|
||||
SET_CURRENT_SUB_PAGE,
|
||||
SET_ROOT_CONFIG,
|
||||
SET_CURRENT_CONFIG_INFO,
|
||||
SET_MODAL_OPEN,
|
||||
SET_LANGUAGE,
|
||||
SET_ITEM_LAYOUT,
|
||||
@ -45,11 +44,11 @@ const {
|
||||
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
config: {}, // The current config, rendered to the UI
|
||||
remoteConfig: {}, // The configuration stored on the server
|
||||
config: {}, // The current config being used, and rendered to the UI
|
||||
rootConfig: null, // Always the content of main config file, never used directly
|
||||
editMode: false, // While true, the user can drag and edit items + sections
|
||||
modalOpen: false, // KB shortcut functionality will be disabled when modal is open
|
||||
currentConfigInfo: undefined, // For multi-page support, will store info about config file
|
||||
currentConfigInfo: {}, // For multi-page support, will store info about config file
|
||||
navigateConfToTab: undefined, // Used to switch active tab in config modal
|
||||
},
|
||||
getters: {
|
||||
@ -68,17 +67,14 @@ const store = new Vuex.Store({
|
||||
return filterUserSections(state.config.sections || []);
|
||||
},
|
||||
pages(state) {
|
||||
return state.remoteConfig.pages || [];
|
||||
return state.config.pages || [];
|
||||
},
|
||||
theme(state) {
|
||||
let localTheme = null;
|
||||
if (state.currentConfigInfo?.pageId) {
|
||||
const themeStoreKey = `${localStorageKeys.THEME}-${state.currentConfigInfo?.pageId}`;
|
||||
localTheme = localStorage[themeStoreKey];
|
||||
} else {
|
||||
localTheme = localStorage[localStorageKeys.THEME];
|
||||
}
|
||||
return localTheme || state.config.appConfig.theme;
|
||||
const localStorageKey = state.currentConfigInfo.confId
|
||||
? `${localStorageKeys.THEME}-${state.currentConfigInfo.confId}` : localStorageKeys.THEME;
|
||||
const localTheme = localStorage[localStorageKey];
|
||||
// Return either theme from local storage, or from appConfig
|
||||
return localTheme || state.config.appConfig.theme || defaultTheme;
|
||||
},
|
||||
webSearch(state, getters) {
|
||||
return getters.appConfig.webSearch || {};
|
||||
@ -146,14 +142,18 @@ const store = new Vuex.Store({
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
/* Set the master config */
|
||||
[SET_ROOT_CONFIG](state, config) {
|
||||
if (!config.appConfig) config.appConfig = {};
|
||||
state.config = config;
|
||||
},
|
||||
/* The config to display and edit. Will differ from ROOT_CONFIG when using multi-page */
|
||||
[SET_CONFIG](state, config) {
|
||||
if (!config.appConfig) config.appConfig = {};
|
||||
state.config = config;
|
||||
},
|
||||
[SET_REMOTE_CONFIG](state, config) {
|
||||
const notNullConfig = config || {};
|
||||
if (!notNullConfig.appConfig) notNullConfig.appConfig = {};
|
||||
state.remoteConfig = notNullConfig;
|
||||
[SET_CURRENT_CONFIG_INFO](state, subConfigInfo) {
|
||||
state.currentConfigInfo = subConfigInfo;
|
||||
},
|
||||
[SET_LANGUAGE](state, lang) {
|
||||
const newConfig = state.config;
|
||||
@ -276,12 +276,13 @@ const store = new Vuex.Store({
|
||||
config.sections = applyItemId(config.sections);
|
||||
state.config = config;
|
||||
},
|
||||
[SET_THEME](state, themOps) {
|
||||
const { theme, pageId } = themOps;
|
||||
[SET_THEME](state, theme) {
|
||||
const newConfig = { ...state.config };
|
||||
newConfig.appConfig.theme = theme;
|
||||
state.config = newConfig;
|
||||
const themeStoreKey = pageId ? `${localStorageKeys.THEME}-${pageId}` : localStorageKeys.THEME;
|
||||
const pageId = state.currentConfigInfo.confId;
|
||||
const themeStoreKey = pageId
|
||||
? `${localStorageKeys.THEME}-${pageId}` : localStorageKeys.THEME;
|
||||
localStorage.setItem(themeStoreKey, theme);
|
||||
InfoHandler('Theme updated', InfoKeys.VISUAL);
|
||||
},
|
||||
@ -306,42 +307,89 @@ const store = new Vuex.Store({
|
||||
[CONF_MENU_INDEX](state, index) {
|
||||
state.navigateConfToTab = index;
|
||||
},
|
||||
[SET_CURRENT_SUB_PAGE](state, subPageObject) {
|
||||
if (!subPageObject) {
|
||||
// Set theme back to primary when navigating to index page
|
||||
const defaulTheme = localStorage.getItem(localStorageKeys.PRIMARY_THEME);
|
||||
if (defaulTheme) state.config.appConfig.theme = defaulTheme;
|
||||
}
|
||||
state.currentConfigInfo = subPageObject;
|
||||
},
|
||||
[USE_MAIN_CONFIG](state) {
|
||||
if (state.remoteConfig) {
|
||||
state.config = state.remoteConfig;
|
||||
} else {
|
||||
this.dispatch(Keys.INITIALIZE_CONFIG);
|
||||
}
|
||||
/* Set config to rootConfig, by calling initialize with no params */
|
||||
async [USE_MAIN_CONFIG]() {
|
||||
this.dispatch(Keys.INITIALIZE_CONFIG);
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
/* Called when app first loaded. Reads config and sets state */
|
||||
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);
|
||||
/* Fetches the root config file, only ever called by INITIALIZE_CONFIG */
|
||||
async [INITIALIZE_ROOT_CONFIG]({ commit }) {
|
||||
// Load and parse config from root config file
|
||||
const configFilePath = process.env.VUE_APP_CONFIG_PATH || '/conf.yml';
|
||||
const data = await yaml.load((await axios.get(configFilePath)).data);
|
||||
// Replace missing root properties with empty objects
|
||||
if (!data.appConfig) data.appConfig = {};
|
||||
if (!data.pageInfo) data.pageInfo = {};
|
||||
if (!data.sections) data.sections = [];
|
||||
// Set the state, and return data
|
||||
commit(SET_ROOT_CONFIG, data);
|
||||
return data;
|
||||
},
|
||||
/* Fetch config for a sub-page (sections and pageInfo only) */
|
||||
async [INITIALIZE_MULTI_PAGE_CONFIG]({ commit, state }, configPath) {
|
||||
axios.get(configPath).then((response) => {
|
||||
const subConfig = yaml.load(response.data);
|
||||
const pageTheme = subConfig.appConfig?.theme;
|
||||
subConfig.appConfig = state.config.appConfig; // Always use parent appConfig
|
||||
if (pageTheme) subConfig.appConfig.theme = pageTheme; // Apply page theme override
|
||||
commit(SET_CONFIG, subConfig);
|
||||
}).catch((err) => {
|
||||
ErrorHandler(`Unable to load config from '${configPath}'`, err);
|
||||
});
|
||||
/**
|
||||
* Fetches config and updates state
|
||||
* If not on sub-page, will trigger the fetch of main config, then use that
|
||||
* If using sub-page config, then fetch that sub-config, then
|
||||
* override certain fields (appConfig, pages) and update config
|
||||
*/
|
||||
async [INITIALIZE_CONFIG]({ commit, state }, subConfigId) {
|
||||
const rootConfig = state.rootConfig || await this.dispatch(Keys.INITIALIZE_ROOT_CONFIG);
|
||||
if (!subConfigId) { // Use root config as config
|
||||
commit(SET_CONFIG, rootConfig);
|
||||
commit(SET_CURRENT_CONFIG_INFO, {});
|
||||
|
||||
let localSections = [];
|
||||
const localSectionsRaw = localStorage[localStorageKeys.CONF_SECTIONS];
|
||||
if (localSectionsRaw) {
|
||||
try {
|
||||
const json = JSON.parse(localSectionsRaw);
|
||||
if (json.length >= 1) localSections = json;
|
||||
} catch (e) {
|
||||
ErrorHandler('Malformed section data in local storage');
|
||||
}
|
||||
}
|
||||
if (localSections.length > 0) {
|
||||
rootConfig.sections = localSections;
|
||||
}
|
||||
return rootConfig;
|
||||
} else {
|
||||
// Find and format path to fetch sub-config from
|
||||
const subConfigPath = formatConfigPath(rootConfig?.pages?.find(
|
||||
(page) => makePageName(page.name) === subConfigId,
|
||||
)?.path);
|
||||
|
||||
if (!subConfigPath) {
|
||||
ErrorHandler(`Unable to find config for '${subConfigId}'`);
|
||||
return null;
|
||||
}
|
||||
|
||||
axios.get(subConfigPath).then((response) => {
|
||||
// Parse the YAML
|
||||
const configContent = yaml.load(response.data) || {};
|
||||
// Certain values must be inherited from root config
|
||||
const theme = configContent?.appConfig?.theme || rootConfig.appConfig?.theme || 'default';
|
||||
configContent.appConfig = rootConfig.appConfig;
|
||||
configContent.pages = rootConfig.pages;
|
||||
configContent.appConfig.theme = theme;
|
||||
|
||||
// Load local sections if they exist
|
||||
const localSectionsRaw = localStorage[`${localStorageKeys.CONF_SECTIONS}-${subConfigId}`];
|
||||
if (localSectionsRaw) {
|
||||
try {
|
||||
const json = JSON.parse(localSectionsRaw);
|
||||
if (json.length >= 1) configContent.sections = json;
|
||||
} catch (e) {
|
||||
ErrorHandler('Malformed section data in local storage for sub-config');
|
||||
}
|
||||
}
|
||||
// Set the config
|
||||
commit(SET_CONFIG, configContent);
|
||||
commit(SET_CURRENT_CONFIG_INFO, { confPath: subConfigPath, confId: subConfigId });
|
||||
}).catch((err) => {
|
||||
ErrorHandler(`Unable to load config from '${subConfigPath}'`, err);
|
||||
});
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
modules: {},
|
||||
|
@ -1619,6 +1619,229 @@ html[data-theme='lissy'] {
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme='glass'],
|
||||
html[data-theme='glass-2'],
|
||||
html[data-theme='neomorphic'] {
|
||||
--primary: #fff;
|
||||
--item-group-outer-background: rgba(0, 0, 0, 0.25);
|
||||
--item-group-background: transparent;
|
||||
--item-group-heading-text-color: #fff;
|
||||
--item-group-heading-text-color-hover: #ffffffd6;
|
||||
--item-group-shadow: 5px 2px 20px rgba(0, 0, 0, 0.5);
|
||||
--background: #190842;
|
||||
--background-darker: #190842;
|
||||
--settings-background: transparent;
|
||||
--search-container-background: transparent;
|
||||
--font-headings: 'Segoe UI', 'Ariel', 'sans-serif';
|
||||
--font-body: 'Roboto', 'Segoe UI', 'Ariel', 'sans-serif';
|
||||
--minimal-view-background-color: transparent;
|
||||
--minimal-view-group-background: rgba(255, 255, 255, 0.15);
|
||||
--minimal-view-section-heading-background: rgba(255, 255, 255, 0.15);
|
||||
--minimal-view-section-heading-color: rgba(255, 255, 255, 0.15);
|
||||
--config-settings-background: #16073de3;
|
||||
--cloud-backup-background: #16073de3;
|
||||
|
||||
@mixin item-transition-styles($bg: transparent, $hover-bg: rgba(255, 255, 255, 0.15), $hover-shadow: rgba(0, 0, 0, 0.75)) {
|
||||
background: $bg;
|
||||
border: 1px solid transparent;
|
||||
box-shadow: none;
|
||||
transition: 0.2s all ease-in-out;
|
||||
|
||||
&:hover {
|
||||
border-radius: 0.35rem;
|
||||
box-shadow: 0 4px 30px $hover-shadow;
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.19);
|
||||
background: $hover-bg;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin transform-scale($normal-scale: 1, $hover-scale: 1.25) {
|
||||
transition: 0.1s all ease-in-out;
|
||||
transform: scale($normal-scale);
|
||||
|
||||
&:hover {
|
||||
transform: scale($hover-scale);
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background-size: cover;
|
||||
background-color: #090317;
|
||||
.home {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-outer, header, .dashy-modal, .dashy-modal .tabs {
|
||||
background: transparent;
|
||||
// backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
// Minimal view components
|
||||
.minimal-section-inner, div.minimal-section-heading {
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(145, 145, 145, 0.45);
|
||||
border-bottom: none;
|
||||
|
||||
&.selected {
|
||||
border: 1px solid rgba(145, 145, 145, 0.45);
|
||||
background: var(--minimal-view-group-background);
|
||||
}
|
||||
}
|
||||
|
||||
.minimal-section-heading {
|
||||
color: var(--minimal-view-section-heading-background);
|
||||
|
||||
&.selected {
|
||||
.section-icon, .section-title {
|
||||
color: var(--primary) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
--glass-button-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
|
||||
--glass-button-hover-shadow: 2px 2px 5px rgba(0, 0, 0, 0.7);
|
||||
|
||||
// Forms and inputs
|
||||
button.save-button,
|
||||
.action-buttons button,
|
||||
.cloud-backup-restore-wrapper button,
|
||||
.tab__nav__item,
|
||||
div.input-container input.input-field,
|
||||
form.normal input,
|
||||
.nav-outer nav .nav-item,
|
||||
div.edit-mode-bottom-banner .edit-banner-section button,
|
||||
.v-select.theme-dropdown.vs__dropdown-toggle,
|
||||
.theme-dropdown div.vs__dropdown-toggle,
|
||||
.config-buttons > svg,
|
||||
.display-options svg,
|
||||
form.minimal input,
|
||||
a.config-button, button.config-button {
|
||||
border-radius: 0.35rem;
|
||||
box-shadow: var(--glass-button-shadow);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.19);
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
transition: all 0.2s ease-in-out;
|
||||
&:hover, &.selected {
|
||||
box-shadow: var(--glass-button-hover-shadow);
|
||||
border: 1px solid rgba(255, 255, 255, 0.25) !important;
|
||||
background: #ffffff42 !important;
|
||||
color: var(--primary) !important;
|
||||
path { fill: var(--primary); }
|
||||
}
|
||||
}
|
||||
|
||||
.tab__nav__items {
|
||||
gap: 1rem;
|
||||
margin: 0.5rem 0 0;
|
||||
.tab__nav__item {
|
||||
padding: 0.5rem 0.5rem;
|
||||
&:hover, .active, .active:hover {
|
||||
background: #ffffff42 !important;
|
||||
span { color: var(--primary) !important; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-options-container .config-buttons, div.cloud-backup-restore-wrapper {
|
||||
background: none;
|
||||
}
|
||||
|
||||
// Item and collapsable specific styles
|
||||
.item {
|
||||
@include item-transition-styles(transparent, rgba(255, 255, 255, 0.15), rgba(0, 0, 0, 0.75));
|
||||
.item-icon {
|
||||
@include transform-scale(1.1, 1.25);
|
||||
}
|
||||
}
|
||||
|
||||
.collapsable {
|
||||
border-radius: 0.5rem;
|
||||
border: 1px solid rgba(0, 0, 0, 0.45);
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
// Modal specific styles
|
||||
.dashy-modal {
|
||||
box-shadow: 0 20px 40px -2px #000000b8, 1px 1px 6px #000000a6 !important;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
background: var(--config-settings-background);
|
||||
}
|
||||
|
||||
.theme-configurator-wrapper, .view-switcher {
|
||||
backdrop-filter: blur(10px);
|
||||
background: var(--config-settings-background);
|
||||
border: 1px solid rgba(255, 255, 255, 0.19);
|
||||
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.edit-mode-top-banner {
|
||||
backdrop-filter: blur(10px);
|
||||
background: #ffffff6b;
|
||||
border-bottom: 1px solid black;
|
||||
|
||||
span { color: #eaff9d; }
|
||||
}
|
||||
|
||||
div.edit-mode-bottom-banner, .add-new-section {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
backdrop-filter: blur(50px);
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme='glass'] {
|
||||
body {
|
||||
background: url('https://zeabur.com/images/bg.png') center center no-repeat;
|
||||
background-size: cover;
|
||||
background-color: #090317;
|
||||
.home {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme='glass-2'] {
|
||||
body {
|
||||
background: url('https://i.ibb.co/FnLH6bj/dashy-glass.jpg') center center no-repeat;
|
||||
background-size: cover;
|
||||
background-color: #090317;
|
||||
}
|
||||
}
|
||||
|
||||
html[data-theme='neomorphic'] {
|
||||
--primary: #fff;
|
||||
--item-group-outer-background: rgba(255, 255, 255, 0.15);
|
||||
--item-group-background: transparent;
|
||||
--item-group-heading-text-color: #fff;
|
||||
--item-group-shadow: 5px 2px 20px rgba(0, 0, 0, 0.5);
|
||||
--background: #5b56f7;
|
||||
// --background: #4bdbfd;
|
||||
--background-darker: #12103c;
|
||||
--settings-background: transparent;
|
||||
--search-container-background: transparent;
|
||||
--font-headings: 'Segoe UI', 'Ariel', 'sans-serif';
|
||||
--font-body: 'Roboto', 'Segoe UI', 'Ariel', 'sans-serif';
|
||||
--minimal-view-background-color: transparent;
|
||||
--minimal-view-group-background: rgba(255, 255, 255, 0.15);
|
||||
--minimal-view-section-heading-background: rgba(255, 255, 255, 0.15);
|
||||
--minimal-view-section-heading-color: rgba(255, 255, 255, 0.15);
|
||||
--config-settings-background: #1fb8f4e3;
|
||||
--cloud-backup-background: #16073de3;
|
||||
|
||||
--glass-button-shadow: 0px 1px 5px rgba(0, 0, 0, 0.5);
|
||||
--glass-button-hover-shadow: 2px 2px 5px rgba(0, 0, 0, 0.7);
|
||||
body {
|
||||
background: var(--background);
|
||||
}
|
||||
.item:hover { box-shadow: 0 3px 10px rgba(0, 0, 0, 0.5); }
|
||||
.collapsable { border: 1px solid rgba(255, 255, 255, 0.25) !important; }
|
||||
}
|
||||
|
||||
|
||||
html[data-theme='cherry-blossom'] {
|
||||
--primary: #e1e8ee;
|
||||
--background: #11171d;
|
||||
|
@ -11,6 +11,8 @@ const getAppConfig = () => {
|
||||
return config.appConfig || {};
|
||||
};
|
||||
|
||||
// const appConfig = $store.getters.appConfig || {};
|
||||
|
||||
/**
|
||||
* Called when the user is still using array for users, prints warning
|
||||
* This was a breaking change, implemented in V 1.6.5
|
||||
|
@ -16,11 +16,9 @@ import ErrorHandler from '@/utils/ErrorHandler';
|
||||
import { applyItemId } from '@/utils/SectionHelpers';
|
||||
import $store from '@/store';
|
||||
|
||||
import buildConf from '../../public/conf.yml';
|
||||
|
||||
export default class ConfigAccumulator {
|
||||
constructor() {
|
||||
this.conf = $store.state.remoteConfig;
|
||||
this.conf = $store.state.config;
|
||||
}
|
||||
|
||||
pages() {
|
||||
@ -33,9 +31,10 @@ export default class ConfigAccumulator {
|
||||
// Set app config from file
|
||||
if (this.conf && this.conf.appConfig) {
|
||||
appConfigFile = this.conf.appConfig;
|
||||
} else if (buildConf && buildConf.appConfig) {
|
||||
appConfigFile = buildConf.appConfig;
|
||||
}
|
||||
// else if (buildConf && buildConf.appConfig) {
|
||||
// appConfigFile = buildConf.appConfig;
|
||||
// }
|
||||
// Fill in defaults if anything missing
|
||||
let usersAppConfig = defaultAppConfig;
|
||||
if (localStorage[localStorageKeys.APP_CONFIG]) {
|
||||
|
@ -1,10 +1,10 @@
|
||||
import ConfigAccumulator from '@/utils/ConfigAccumalator';
|
||||
// import $store from '@/store';
|
||||
import filterUserSections from '@/utils/CheckSectionVisibility';
|
||||
import { languages } from '@/utils/languages';
|
||||
import {
|
||||
visibleComponents,
|
||||
localStorageKeys,
|
||||
theme as defaultTheme,
|
||||
language as defaultLanguage,
|
||||
} from '@/utils/defaults';
|
||||
import ErrorHandler from '@/utils/ErrorHandler';
|
||||
@ -26,6 +26,13 @@ export const makePageSlug = (pageName, pageType) => {
|
||||
return `/${pageType}/${formattedName}`;
|
||||
};
|
||||
|
||||
/* Put fetch path for additional configs in correct format */
|
||||
export const formatConfigPath = (configPath) => {
|
||||
if (configPath.includes('http')) return configPath;
|
||||
if (configPath.substring(0, 1) !== '/') return `/${configPath}`;
|
||||
return configPath;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initiates the Accumulator class and generates a complete config object
|
||||
* Self-executing function, returns the full user config as a JSON object
|
||||
@ -67,34 +74,12 @@ export const componentVisibility = (appConfig) => {
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the users saved theme, first looks for local storage theme,
|
||||
* then looks at user's appConfig, and finally checks the defaults
|
||||
* @returns {string} Name of theme to apply
|
||||
*/
|
||||
export const getTheme = () => {
|
||||
const localTheme = localStorage[localStorageKeys.THEME];
|
||||
const appConfigTheme = config.appConfig.theme;
|
||||
return localTheme || appConfigTheme || defaultTheme;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets any custom styles the user has applied, wither from local storage, or from the config
|
||||
* @returns {object} An array of objects, one for each theme, containing kvps for variables
|
||||
*/
|
||||
export const getCustomColors = () => {
|
||||
const localColors = JSON.parse(localStorage[localStorageKeys.CUSTOM_COLORS] || '{}');
|
||||
const configColors = config.appConfig.customColors || {};
|
||||
return Object.assign(configColors, localColors);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a list of items which the user has assigned a hotkey to
|
||||
* So that when the hotkey is pressed, the app/ service can be launched
|
||||
*/
|
||||
export const getCustomKeyShortcuts = () => {
|
||||
export const getCustomKeyShortcuts = (sections) => {
|
||||
const results = [];
|
||||
const sections = config.sections || [];
|
||||
sections.forEach((section) => {
|
||||
const itemsWithHotKeys = section.items.filter(item => item.hotkey);
|
||||
results.push(itemsWithHotKeys.map(item => ({ hotkey: item.hotkey, url: item.url })));
|
||||
|
@ -534,6 +534,37 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"enableHeaderAuth": {
|
||||
"title": "Enable HeaderAuth?",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "If set to true, enable Header Authentication. See appConfig.auth.headerAuth"
|
||||
},
|
||||
"headerAuth": {
|
||||
"type": "object",
|
||||
"description": "Configuration for headerAuth",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"proxyWhitelist"
|
||||
],
|
||||
"properties": {
|
||||
"userHeader": {
|
||||
"title": "User Header",
|
||||
"type": "string",
|
||||
"description": "Header name which contains username",
|
||||
"default": "REMOTE_USER"
|
||||
},
|
||||
"proxyWhitelist": {
|
||||
"title": "Upstream Proxy Auth Trust",
|
||||
"type": "array",
|
||||
"description": "Upstream proxy servers to expect authenticated requests from",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"description": "IPs of upstream proxies that will be trusted"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"enableKeycloak": {
|
||||
"title": "Enable Keycloak?",
|
||||
"type": "boolean",
|
||||
@ -982,6 +1013,11 @@
|
||||
"type": "number",
|
||||
"description": "A numeric shortcut key, between 0 and 9. Useful for quickly launching frequently used applications"
|
||||
},
|
||||
"rel": {
|
||||
"title": "rel",
|
||||
"type": "string",
|
||||
"description": "The rel attribute for the link. For specifying the relationship between the current document and the linked document"
|
||||
},
|
||||
"tags": {
|
||||
"title": "Tags",
|
||||
"type": "array",
|
||||
@ -1086,6 +1122,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"filteredItems": {
|
||||
"title": "Filtered Items - temp",
|
||||
"type": "array",
|
||||
"description": "This attribute will be deprecated in the next release - do not use!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ export const welcomeMsg = () => {
|
||||
/* Prints warning message, usually when there is a configuration error */
|
||||
export const warningMsg = (message, stack) => {
|
||||
console.info(
|
||||
`\n%c⚠️ Warning ⚠️%c \n${message} \n\n%cThis is likely not an issue with Dashy, but rather your configuration. If you think it is a bug, please open a ticket on GitHub: https://git.io/JukXk`,
|
||||
`\n%c⚠️ Warning ⚠️%c \n${message} \n\n%cThis is likely not an issue with Dashy, but rather your configuration.\nIf you think it is a bug, please open a ticket on GitHub: https://git.io/JukXk`,
|
||||
"color:#ceb73f; background: #ceb73f33; font-size:1.5rem; padding:0.15rem; margin: 1rem auto; font-family: Rockwell, Tahoma, 'Trebuchet MS', Helvetica; border: 2px solid #ceb73f; border-radius: 4px; font-weight: bold; text-shadow: 1px 1px 1px #000000bf;",
|
||||
'font-weight: bold; font-size: 1rem;color: #ceb73f;',
|
||||
"color: #ceb73f; font-size: 0.75rem; font-family: Tahoma, 'Trebuchet MS', Helvetica;",
|
||||
|
@ -9,12 +9,12 @@
|
||||
|
||||
/* eslint-disable global-require */
|
||||
|
||||
import ConfigAccumulator from '@/utils/ConfigAccumalator';
|
||||
import $store from '@/store';
|
||||
import { sentryDsn } from '@/utils/defaults';
|
||||
|
||||
const ErrorReporting = (Vue, router) => {
|
||||
// Fetch users config
|
||||
const appConfig = new ConfigAccumulator().appConfig() || {};
|
||||
const appConfig = $store.getters.appConfig || {};
|
||||
// Check if error reporting is enabled. Only proceed if user has turned it on.
|
||||
if (appConfig.enableErrorReporting) {
|
||||
// Get current app version
|
||||
|
78
src/utils/HeaderAuth.js
Normal file
78
src/utils/HeaderAuth.js
Normal file
@ -0,0 +1,78 @@
|
||||
import axios from 'axios';
|
||||
import sha256 from 'crypto-js/sha256';
|
||||
import ConfigAccumulator from '@/utils/ConfigAccumalator';
|
||||
import { cookieKeys, localStorageKeys, serviceEndpoints } from '@/utils/defaults';
|
||||
import { InfoHandler, ErrorHandler, InfoKeys } from '@/utils/ErrorHandler';
|
||||
import { logout } from '@/utils/Auth';
|
||||
|
||||
const getAppConfig = () => {
|
||||
const Accumulator = new ConfigAccumulator();
|
||||
const config = Accumulator.config();
|
||||
return config.appConfig || {};
|
||||
};
|
||||
|
||||
class HeaderAuth {
|
||||
constructor() {
|
||||
const { auth } = getAppConfig();
|
||||
const {
|
||||
userHeader, proxyWhitelist,
|
||||
} = auth.headerAuth;
|
||||
this.userHeader = userHeader;
|
||||
this.proxyWhitelist = proxyWhitelist;
|
||||
this.users = auth.users;
|
||||
}
|
||||
|
||||
/* eslint-disable class-methods-use-this */
|
||||
login() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const baseUrl = process.env.VUE_APP_DOMAIN || window.location.origin;
|
||||
axios.get(`${baseUrl}${serviceEndpoints.getUser}`).then((response) => {
|
||||
if (!response.data) {
|
||||
reject(Error('Error, expected data nout returned'));
|
||||
} else if (response.data.errorMsg) {
|
||||
reject(response.data.errorMsg);
|
||||
} else {
|
||||
try {
|
||||
this.users.forEach((user) => {
|
||||
if (user.user.toLowerCase() === response.data.user.toLowerCase()) { // User found
|
||||
const strAndUpper = (input) => input.toString().toUpperCase();
|
||||
const sha = strAndUpper(sha256(strAndUpper(user.user) + strAndUpper(user.hash)));
|
||||
document.cookie = `${cookieKeys.AUTH_TOKEN}=${sha};`;
|
||||
localStorage.setItem(localStorageKeys.USERNAME, user.user);
|
||||
InfoHandler(`Successfully signed in as ${response.data.user}`, InfoKeys.AUTH);
|
||||
resolve(response.data.user);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
logout() {
|
||||
logout();
|
||||
}
|
||||
}
|
||||
|
||||
export const isHeaderAuthEnabled = () => {
|
||||
const { auth } = getAppConfig();
|
||||
if (!auth) return false;
|
||||
return auth.enableHeaderAuth || false;
|
||||
};
|
||||
|
||||
let headerAuth;
|
||||
|
||||
export const initHeaderAuth = () => {
|
||||
headerAuth = new HeaderAuth();
|
||||
return headerAuth.login();
|
||||
};
|
||||
|
||||
// TODO: Find where this is implemented
|
||||
export const getHeaderAuth = () => {
|
||||
if (!headerAuth) {
|
||||
ErrorHandler("HeaderAuth not initialized, can't get instance of class");
|
||||
}
|
||||
return headerAuth;
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
/* Dashy: Licensed under MIT, (C) Alicia Sykes 2021 <https://aliciasykes.com> */
|
||||
/* Dashy: Licensed under MIT, (C) Alicia Sykes 2024 <https://aliciasykes.com> */
|
||||
|
||||
/* Tile filtering utility */
|
||||
import ErrorHandler from '@/utils/ErrorHandler';
|
||||
|
@ -1,9 +1,11 @@
|
||||
// A list of mutation names
|
||||
const KEY_NAMES = [
|
||||
'INITIALIZE_CONFIG',
|
||||
'INITIALIZE_ROOT_CONFIG',
|
||||
'INITIALIZE_MULTI_PAGE_CONFIG',
|
||||
'SET_CONFIG',
|
||||
'SET_REMOTE_CONFIG',
|
||||
'SET_ROOT_CONFIG',
|
||||
'SET_CURRENT_CONFIG_INFO',
|
||||
'SET_CURRENT_SUB_PAGE',
|
||||
'SET_MODAL_OPEN',
|
||||
'SET_LANGUAGE',
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user