diff --git a/Dockerfile b/Dockerfile index 48e5d2f3..1d9a4931 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,12 +7,10 @@ WORKDIR /app COPY --link package.json pnpm-lock.yaml* ./ -RUN < config/settings.yaml - NEXT_PUBLIC_BUILDTIME=$BUILDTIME NEXT_PUBLIC_VERSION=$VERSION NEXT_PUBLIC_REVISION=$REVISION npm run build -EOF +SHELL ["/bin/ash", "-xeo", "pipefail", "-c"] +RUN npm run telemetry \ + && mkdir config && echo '---' > config/settings.yaml \ + && NEXT_PUBLIC_BUILDTIME=$BUILDTIME NEXT_PUBLIC_VERSION=$VERSION NEXT_PUBLIC_REVISION=$REVISION npm run build # Production image, copy all the files and run next FROM docker.io/node:18-alpine AS runner @@ -50,12 +46,15 @@ ENV NODE_ENV production WORKDIR /app # Copy files from context (this allows the files to copy before the builder stage is done). -COPY --link package.json next.config.js ./ -COPY --link /public ./public +COPY --link --chown=1000:1000 package.json next.config.js ./ +COPY --link --chown=1000:1000 /public ./public/ # Copy files from builder -COPY --link --from=builder /app/.next/standalone ./ -COPY --link --from=builder /app/.next/static/ ./.next/static/ +COPY --link --from=builder --chown=1000:1000 /app/.next/standalone ./ +COPY --link --from=builder --chown=1000:1000 /app/.next/static/ ./.next/static/ +COPY --link --chmod=755 docker-entrypoint.sh /usr/local/bin/ + +RUN apk add --no-cache su-exec ENV PORT 3000 EXPOSE $PORT @@ -63,4 +62,5 @@ EXPOSE $PORT HEALTHCHECK --interval=10s --timeout=3s --start-period=20s \ CMD wget --no-verbose --tries=1 --spider --no-check-certificate http://localhost:$PORT/api/healthcheck || exit 1 +ENTRYPOINT ["docker-entrypoint.sh"] CMD ["node", "server.js"] diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 5603d7e5..bf443461 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -2,8 +2,22 @@ set -e +# Default to root, so old installations won't break +export PUID=${PUID:-0} +export PGID=${PGID:-0} + # This is in attempt to preserve the original behavior of the Dockerfile, # while also supporting the lscr.io /config directory [ ! -d "/app/config" ] && ln -s /config /app/config -node server.js +# Set privileges for /app but only if pid 1 user is root and we are dropping privileges. +# If container is run as an unprivileged user, it means owner already handled ownership setup on their own. +# Running chown in that case (as non-root) will cause error +[ "$(id -u)" == "0" ] && [ "${PUID}" != "0" ] && chown -R ${PUID}:${PGID} /app + +# Drop privileges (when asked to) if root, otherwise run as current user +if [ "$(id -u)" == "0" ] && [ "${PUID}" != "0" ]; then + su-exec ${PUID}:${PGID} "$@" +else + exec "$@" +fi diff --git a/public/locales/ar/common.json b/public/locales/ar/common.json index 853ad4c7..3e9f3a66 100644 --- a/public/locales/ar/common.json +++ b/public/locales/ar/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/bg/common.json b/public/locales/bg/common.json index c30b4c3d..4ee07017 100644 --- a/public/locales/bg/common.json +++ b/public/locales/bg/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "print_progress": "Progress", + "printer_state": "Printer State", + "print_status": "Print Status", + "layers": "Layers" } } diff --git a/public/locales/ca/common.json b/public/locales/ca/common.json index 2b2fdd9e..f7c09cee 100644 --- a/public/locales/ca/common.json +++ b/public/locales/ca/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/cs/common.json b/public/locales/cs/common.json index 170c4a21..f749e0f5 100644 --- a/public/locales/cs/common.json +++ b/public/locales/cs/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "print_progress": "Progress", + "printer_state": "Printer State", + "print_status": "Print Status", + "layers": "Layers" } } diff --git a/public/locales/da/common.json b/public/locales/da/common.json index c8845105..fe967fe0 100644 --- a/public/locales/da/common.json +++ b/public/locales/da/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/de/common.json b/public/locales/de/common.json index e32ba18b..8037d4c8 100644 --- a/public/locales/de/common.json +++ b/public/locales/de/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 032c1dfe..a07633ce 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -412,5 +412,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/eo/common.json b/public/locales/eo/common.json index a073234f..11e7ad54 100644 --- a/public/locales/eo/common.json +++ b/public/locales/eo/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "layers": "Layers", + "print_progress": "Progress" } } diff --git a/public/locales/es/common.json b/public/locales/es/common.json index c0026c8f..34d69f89 100644 --- a/public/locales/es/common.json +++ b/public/locales/es/common.json @@ -403,5 +403,11 @@ "memory": "Memoria activa", "wanUpload": "Carga WAN", "wanDownload": "Descargar WAN" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/fi/common.json b/public/locales/fi/common.json index f7871dac..161d31a0 100644 --- a/public/locales/fi/common.json +++ b/public/locales/fi/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/fr/common.json b/public/locales/fr/common.json index ab46de39..28c829ce 100644 --- a/public/locales/fr/common.json +++ b/public/locales/fr/common.json @@ -403,5 +403,11 @@ "memory": "Mém. Utilisée", "wanUpload": "WAN Envoi", "wanDownload": "WAN Récep." + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/he/common.json b/public/locales/he/common.json index e211074c..3ecf387d 100644 --- a/public/locales/he/common.json +++ b/public/locales/he/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/hi/common.json b/public/locales/hi/common.json index 098362ee..c876d79d 100644 --- a/public/locales/hi/common.json +++ b/public/locales/hi/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/hr/common.json b/public/locales/hr/common.json index 68a0b21a..de22db79 100644 --- a/public/locales/hr/common.json +++ b/public/locales/hr/common.json @@ -77,13 +77,13 @@ "emby": { "playing": "Reprodukcija", "transcoding": "Prekodiranje", - "bitrate": "Brzina prijenosa", + "bitrate": "Stopa bitova", "no_active": "Nema aktivnih prijenosa" }, "tautulli": { "playing": "Reprodukcija", "transcoding": "Prekodiranje", - "bitrate": "Brzina prijenosa", + "bitrate": "Stopa bitova", "no_active": "Nema aktivnih prijenosa" }, "nzbget": { @@ -388,10 +388,10 @@ "seed": "Prenošenje preuzetog sadržaja" }, "mikrotik": { - "cpuLoad": "CPU Load", + "cpuLoad": "CPU opterećenje", "memoryUsed": "Korištena memorija", - "uptime": "Uptime", - "numberOfLeases": "Leases" + "uptime": "Radno vrijeme", + "numberOfLeases": "Unajmljivanja" }, "xteve": { "streams_all": "Svi prijenosi", @@ -399,9 +399,15 @@ "streams_xepg": "XEPG kanali" }, "opnsense": { - "cpu": "CPU Load", + "cpu": "CPU opterećenje", "memory": "Aktivna memorija", "wanUpload": "WAN prijenos", "wanDownload": "WAN preuzimanje" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/hu/common.json b/public/locales/hu/common.json index 0d77fd4c..912345ab 100644 --- a/public/locales/hu/common.json +++ b/public/locales/hu/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "layers": "Layers", + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress" } } diff --git a/public/locales/it/common.json b/public/locales/it/common.json index 466b2655..7defae31 100644 --- a/public/locales/it/common.json +++ b/public/locales/it/common.json @@ -51,7 +51,7 @@ "total": "Totale", "free": "Libero", "used": "In utilizzo", - "load": "Carica", + "load": "Carico", "cpu": "CPU" }, "rutorrent": { @@ -175,7 +175,7 @@ "strelaysrv": { "numActiveSessions": "Sessioni", "numConnections": "Connessioni", - "dataRelayed": "Ritrasmettessi", + "dataRelayed": "Ritrasmessi", "transferRate": "Velocità" }, "authentik": { @@ -222,10 +222,10 @@ "wmo": { "65-day": "Pioggia Intensa", "2-night": "Parzialmente Nuvoloso", - "0-day": "Solleggiato", - "0-night": "Pulisci", - "1-day": "Principalmente Soleggiato", - "1-night": "Principalmente Sereno", + "0-day": "Soleggiato", + "0-night": "Sereno", + "1-day": "Prevalentemente Soleggiato", + "1-night": "Prevalentemente Sereno", "2-day": "Parzialmente Nuvoloso", "3-day": "Nuvoloso", "3-night": "Nuvoloso", @@ -314,7 +314,7 @@ }, "navidrome": { "nothing_streaming": "Nessun Sistema Attivo", - "please_wait": "Attendere, Prego" + "please_wait": "Attendere prego" }, "pyload": { "speed": "Velocità", @@ -391,17 +391,23 @@ "cpuLoad": "Carico della CPU", "memoryUsed": "Memoria Utilizzata", "uptime": "Tempo di attività", - "numberOfLeases": "Leases" + "numberOfLeases": "Lease" }, "xteve": { - "streams_all": "All Streams", - "streams_active": "Active Streams", - "streams_xepg": "XEPG Channels" + "streams_all": "Tutti gli stream", + "streams_active": "Stream attivi", + "streams_xepg": "Canali XEPG" }, "opnsense": { - "cpu": "CPU Load", - "memory": "Active Memory", + "cpu": "Carico CPU", + "memory": "Memoria in uso", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/ja/common.json b/public/locales/ja/common.json index f6217d93..a372c3f9 100644 --- a/public/locales/ja/common.json +++ b/public/locales/ja/common.json @@ -31,8 +31,8 @@ "public_ip": "Public IP" }, "common": { - "bibyterate": "", - "bibitrate": "" + "bibyterate": "{{value, rate(bits: false; binary: true)}}", + "bibitrate": "{{value, rate(bits: true; binary: true)}}" }, "widget": { "api_error": "APIエラー", @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/ms/common.json b/public/locales/ms/common.json index ed9e4903..6b62fd21 100644 --- a/public/locales/ms/common.json +++ b/public/locales/ms/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/nb-NO/common.json b/public/locales/nb-NO/common.json index 7f9b2c80..e066d970 100644 --- a/public/locales/nb-NO/common.json +++ b/public/locales/nb-NO/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/nl/common.json b/public/locales/nl/common.json index c5c9ea0b..2c09cd50 100644 --- a/public/locales/nl/common.json +++ b/public/locales/nl/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/pl/common.json b/public/locales/pl/common.json index 07ce69f2..d5c58870 100644 --- a/public/locales/pl/common.json +++ b/public/locales/pl/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/pt-BR/common.json b/public/locales/pt-BR/common.json index 68aacbf4..cd49dc17 100644 --- a/public/locales/pt-BR/common.json +++ b/public/locales/pt-BR/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/pt/common.json b/public/locales/pt/common.json index f3cd28f5..6d9229b2 100644 --- a/public/locales/pt/common.json +++ b/public/locales/pt/common.json @@ -412,5 +412,11 @@ "memory": "Memória Ativa", "wanUpload": "Envio WAN", "wanDownload": "WAN Descarga" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/ro/common.json b/public/locales/ro/common.json index 0a1080e1..ef9d6e5a 100644 --- a/public/locales/ro/common.json +++ b/public/locales/ro/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/ru/common.json b/public/locales/ru/common.json index 6fb5d4f4..71923d1d 100644 --- a/public/locales/ru/common.json +++ b/public/locales/ru/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/sr/common.json b/public/locales/sr/common.json index 6e220e04..6a675f01 100644 --- a/public/locales/sr/common.json +++ b/public/locales/sr/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/sv/common.json b/public/locales/sv/common.json index d682d368..eb73e824 100644 --- a/public/locales/sv/common.json +++ b/public/locales/sv/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "print_progress": "Progress", + "printer_state": "Printer State", + "print_status": "Print Status", + "layers": "Layers" } } diff --git a/public/locales/te/common.json b/public/locales/te/common.json index f5c14b37..fb976853 100644 --- a/public/locales/te/common.json +++ b/public/locales/te/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/tr/common.json b/public/locales/tr/common.json index b4d093bb..f8be2a1b 100644 --- a/public/locales/tr/common.json +++ b/public/locales/tr/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/uk/common.json b/public/locales/uk/common.json index fa4c12fb..d136545b 100644 --- a/public/locales/uk/common.json +++ b/public/locales/uk/common.json @@ -23,8 +23,8 @@ "deluge": { "download": "Завантаження", "upload": "Відправлення", - "leech": "Leech", - "seed": "Seed" + "leech": "Ліч", + "seed": "Сід" }, "readarr": { "wanted": "Розшукується", @@ -32,76 +32,76 @@ "books": "Книжки" }, "wmo": { - "55-day": "Heavy Drizzle", - "55-night": "Heavy Drizzle", - "56-day": "Light Freezing Drizzle", - "56-night": "Light Freezing Drizzle", - "0-day": "Sunny", - "0-night": "Clear", - "1-day": "Mainly Sunny", - "1-night": "Mainly Clear", - "2-day": "Partly Cloudy", - "2-night": "Partly Cloudy", - "3-day": "Cloudy", - "3-night": "Cloudy", - "53-day": "Drizzle", - "45-day": "Foggy", - "45-night": "Foggy", - "48-day": "Foggy", - "48-night": "Foggy", - "51-day": "Light Drizzle", - "51-night": "Light Drizzle", - "53-night": "Drizzle", - "57-day": "Freezing Drizzle", - "57-night": "Freezing Drizzle", - "61-day": "Light Rain", - "61-night": "Light Rain", - "63-day": "Rain", - "63-night": "Rain", - "65-day": "Heavy Rain", - "65-night": "Heavy Rain", - "66-day": "Freezing Rain", - "66-night": "Freezing Rain", - "67-day": "Freezing Rain", - "67-night": "Freezing Rain", - "71-day": "Light Snow", - "71-night": "Light Snow", - "73-day": "Snow", - "73-night": "Snow", - "75-day": "Heavy Snow", - "75-night": "Heavy Snow", - "77-day": "Snow Grains", - "77-night": "Snow Grains", - "80-day": "Light Showers", - "80-night": "Light Showers", - "81-day": "Showers", - "82-day": "Heavy Showers", - "82-night": "Heavy Showers", - "81-night": "Showers", - "85-day": "Snow Showers", - "85-night": "Snow Showers", - "86-day": "Snow Showers", - "86-night": "Snow Showers", - "95-day": "Thunderstorm", - "95-night": "Thunderstorm", - "96-day": "Thunderstorm With Hail", - "96-night": "Thunderstorm With Hail", - "99-day": "Thunderstorm With Hail", - "99-night": "Thunderstorm With Hail" + "55-day": "Сильна мряка", + "55-night": "Сильна мряка", + "56-day": "Невеликий морозний дощ", + "56-night": "Невеликий морозний дощ", + "0-day": "Сонячно", + "0-night": "Ясно", + "1-day": "Переважно сонячно", + "1-night": "Переважно ясно", + "2-day": "Частково хмарно", + "2-night": "Частково хмарно", + "3-day": "Хмарно", + "3-night": "Хмарно", + "53-day": "Мряка", + "45-day": "Туманно", + "45-night": "Туманно", + "48-day": "Туманно", + "48-night": "Туманно", + "51-day": "Легка мряка", + "51-night": "Легка мряка", + "53-night": "Мряка", + "57-day": "Морозний дощ", + "57-night": "Морозний дощ", + "61-day": "Невеликий дощ", + "61-night": "Невеликий дощ", + "63-day": "Дощ", + "63-night": "Дощ", + "65-day": "Сильний дощ", + "65-night": "Сильний дощ", + "66-day": "Холодний дощ", + "66-night": "Холодний дощ", + "67-day": "Холодний дощ", + "67-night": "Холодний дощ", + "71-day": "Невеликий сніг", + "71-night": "Невеликий сніг", + "73-day": "Сніг", + "73-night": "Сніг", + "75-day": "Снігопад", + "75-night": "Снігопад", + "77-day": "Снігові зерна", + "77-night": "Снігові зерна", + "80-day": "Невелика злива", + "80-night": "Невелика злива", + "81-day": "Злива", + "82-day": "Сильна злива", + "82-night": "Сильна злива", + "81-night": "Злива", + "85-day": "Дощ зі снігом", + "85-night": "Дощ зі снігом", + "86-day": "Дощ зі снігом", + "86-night": "Дощ зі снігом", + "95-day": "Гроза", + "95-night": "Гроза", + "96-day": "Гроза з градом", + "96-night": "Гроза з градом", + "99-day": "Гроза з градом", + "99-night": "Гроза з градом" }, "pyload": { - "speed": "Speed", - "active": "Active", - "queue": "Queue", - "total": "Total" + "speed": "Швидкість", + "active": "Активно", + "queue": "Черга", + "total": "Всього" }, "gluetun": { - "country": "Country", - "public_ip": "Public IP", - "region": "Region" + "country": "Країна", + "public_ip": "Публічний IP", + "region": "Регіон" }, "hdhomerun": { - "channels": "Channels", + "channels": "Канали", "hd": "HD" }, "widget": { @@ -167,8 +167,8 @@ "flood": { "download": "Завантаження", "upload": "Відправлення", - "leech": "Leech", - "seed": "Seed" + "leech": "Ліч", + "seed": "Сід" }, "changedetectionio": { "totalObserved": "Всього спостережень", @@ -193,20 +193,20 @@ "transmission": { "download": "Завантаження", "upload": "Відправлення", - "leech": "Leech", - "seed": "Seed" + "leech": "Ліч", + "seed": "Сід" }, "qbittorrent": { "download": "Завантаження", "upload": "Відправлення", - "leech": "Leech", - "seed": "Seed" + "leech": "Ліч", + "seed": "Сід" }, "downloadstation": { "download": "Завантаження", "upload": "Відправлення", - "leech": "Leech", - "seed": "Seed" + "leech": "Ліч", + "seed": "Сід" }, "sonarr": { "wanted": "Розшукується", @@ -225,13 +225,13 @@ "albums": "Альбоми" }, "traefik": { - "middleware": "Middleware", + "middleware": "Проміжне програмне забезпечення", "routers": "Роутери", "services": "Сервіси" }, "navidrome": { - "nothing_streaming": "No Active Streams", - "please_wait": "Please Wait" + "nothing_streaming": "Немає активних потоків", + "please_wait": "Будь ласка, зачекайте" }, "bazarr": { "missingEpisodes": "Відсутні епізоди", @@ -281,127 +281,133 @@ "saved": "Збережено" }, "npm": { - "enabled": "Enabled", - "disabled": "Disabled", - "total": "Total" + "enabled": "Увімкнено", + "disabled": "Вимкнено", + "total": "Всього" }, "coinmarketcap": { - "configure": "Configure one or more crypto currencies to track", - "1hour": "1 Hour", - "1day": "1 Day", - "7days": "7 Days", - "30days": "30 Days" + "configure": "Налаштуйте одну або кілька криптовалют для відстеження", + "1hour": "1 година", + "1day": "1 день", + "7days": "7 днів", + "30days": "30 днів" }, "mastodon": { - "domain_count": "Domains", - "user_count": "Users", - "status_count": "Posts" + "domain_count": "Домени", + "user_count": "Користувачі", + "status_count": "Пости" }, "miniflux": { - "read": "Read", - "unread": "Unread" + "read": "Прочитано", + "unread": "Не прочитано" }, "gotify": { - "apps": "Applications", - "clients": "Clients", - "messages": "Messages" + "apps": "Застосунки", + "clients": "Клієнти", + "messages": "Повідомлення" }, "prowlarr": { - "enableIndexers": "Indexers", - "numberOfGrabs": "Grabs", - "numberOfQueries": "Queries", - "numberOfFailGrabs": "Fail Grabs", - "numberOfFailQueries": "Fail Queries" + "enableIndexers": "Індексатори", + "numberOfGrabs": "Захоплення", + "numberOfQueries": "Запити", + "numberOfFailGrabs": "Невдалі захоплення", + "numberOfFailQueries": "Невдалі запити" }, "jackett": { - "configured": "Configured", - "errored": "Errored" + "configured": "Налаштовано", + "errored": "Помилка" }, "strelaysrv": { - "numActiveSessions": "Sessions", - "numConnections": "Connections", - "dataRelayed": "Relayed", - "transferRate": "Rate" + "numActiveSessions": "Сесії", + "numConnections": "Підключення", + "dataRelayed": "Ретрансльовано", + "transferRate": "Швидкість" }, "authentik": { - "users": "Users", - "loginsLast24H": "Logins (24h)", - "failedLoginsLast24H": "Failed Logins (24h)" + "users": "Користувачі", + "loginsLast24H": "Вхід (24 години)", + "failedLoginsLast24H": "Невдалі входи (24 години)" }, "proxmox": { - "mem": "MEM", + "mem": "Пам'ять", "cpu": "CPU", - "vms": "VMs", - "lxc": "LXC" + "vms": "Віртуальні машини", + "lxc": "Контейнери Linux" }, "glances": { "cpu": "CPU", - "mem": "MEM", - "wait": "Please wait" + "mem": "Пам'ять", + "wait": "Будь ласка, зачекайте" }, "quicklaunch": { - "bookmark": "Bookmark", - "service": "Service" + "bookmark": "Закладка", + "service": "Сервіс" }, "homebridge": { - "available_update": "System", - "updates": "Updates", + "available_update": "Система", + "updates": "Оновлення", "child_bridges_status": "{{ok}}/{{total}}", - "update_available": "Update Available", - "up_to_date": "Up to Date", - "child_bridges": "Child Bridges" + "update_available": "Доступне оновлення", + "up_to_date": "Актуально", + "child_bridges": "Дитячі мости" }, "watchtower": { - "containers_scanned": "Scanned", - "containers_updated": "Updated", - "containers_failed": "Failed" + "containers_scanned": "Відскановано", + "containers_updated": "Оновлено", + "containers_failed": "Невдача" }, "autobrr": { - "approvedPushes": "Approved", - "rejectedPushes": "Rejected", - "filters": "Filters", - "indexers": "Indexers" + "approvedPushes": "Схвалено", + "rejectedPushes": "Відхилено", + "filters": "Фільтри", + "indexers": "Індексатори" }, "tubearchivist": { - "downloads": "Queue", - "videos": "Videos", - "channels": "Channels", - "playlists": "Playlists" + "downloads": "Черга", + "videos": "Відео", + "channels": "Канали", + "playlists": "Плейлисти" }, "truenas": { - "load": "System Load", - "uptime": "Uptime", - "alerts": "Alerts", + "load": "Завантаження системи", + "uptime": "Час роботи", + "alerts": "Сповіщення", "time": "{{value, number(style: unit; unitDisplay: long;)}}" }, "scrutiny": { - "passed": "Passed", - "failed": "Failed", - "unknown": "Unknown" + "passed": "Пройшов", + "failed": "Невдача", + "unknown": "Невідомо" }, "paperlessngx": { - "inbox": "Inbox", - "total": "Total" + "inbox": "Вхідні", + "total": "Всього" }, "nextdns": { - "wait": "Please Wait", - "no_devices": "No Device Data Received" + "wait": "Будь ласка, зачекайте", + "no_devices": "Дані про пристрій не отримано" }, "mikrotik": { - "cpuLoad": "CPU Load", - "memoryUsed": "Memory Used", - "uptime": "Uptime", - "numberOfLeases": "Leases" + "cpuLoad": "Завантаження CPU", + "memoryUsed": "Використана пам'ять", + "uptime": "Час роботи", + "numberOfLeases": "Оренди" }, "xteve": { - "streams_all": "All Streams", - "streams_active": "Active Streams", - "streams_xepg": "XEPG Channels" + "streams_all": "Всі потоки", + "streams_active": "Активні потоки", + "streams_xepg": "Канали XEPG" }, "opnsense": { - "cpu": "CPU Load", - "memory": "Active Memory", - "wanUpload": "WAN Upload", - "wanDownload": "WAN Download" + "cpu": "Завантаження CPU", + "memory": "Активна пам'ять", + "wanUpload": "Вивантаження WAN", + "wanDownload": "Завантаження WAN" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/vi/common.json b/public/locales/vi/common.json index 870e3fe5..2c2c64aa 100644 --- a/public/locales/vi/common.json +++ b/public/locales/vi/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/yue/common.json b/public/locales/yue/common.json index 1b346827..77727d4a 100644 --- a/public/locales/yue/common.json +++ b/public/locales/yue/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "print_progress": "Progress", + "layers": "Layers", + "printer_state": "Printer State", + "print_status": "Print Status" } } diff --git a/public/locales/zh-CN/common.json b/public/locales/zh-CN/common.json index cf4c4a3f..abad4266 100644 --- a/public/locales/zh-CN/common.json +++ b/public/locales/zh-CN/common.json @@ -403,5 +403,11 @@ "memory": "Active Memory", "wanUpload": "WAN Upload", "wanDownload": "WAN Download" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/public/locales/zh-Hant/common.json b/public/locales/zh-Hant/common.json index fd00cccd..ff14d256 100644 --- a/public/locales/zh-Hant/common.json +++ b/public/locales/zh-Hant/common.json @@ -1,407 +1,413 @@ { "widget": { - "missing_type": "Missing Widget Type: {{type}}", - "api_error": "API Error", - "status": "Status", - "information": "Information", + "missing_type": "遺失小工具的類型: {{type}}", + "api_error": "API錯誤", + "status": "狀態", + "information": "資訊", "url": "URL", "raw_error": "Raw Error", "response_data": "Response Data" }, "weather": { - "current": "Current Location", - "allow": "Click to allow", - "updating": "Updating", - "wait": "Please wait" + "current": "目前位置", + "allow": "點擊以允許", + "updating": "更新中", + "wait": "請稍後" }, "docker": { - "rx": "RX", - "offline": "Offline", - "tx": "TX", - "mem": "MEM", - "cpu": "CPU", - "error": "Error", - "unknown": "Unknown" + "rx": "接收", + "offline": "離線", + "tx": "發送", + "mem": "記憶體", + "cpu": "處理器", + "error": "錯誤", + "unknown": "未知的" }, "emby": { - "playing": "Playing", - "transcoding": "Transcoding", - "bitrate": "Bitrate", - "no_active": "No Active Streams" + "playing": "正在播放", + "transcoding": "轉碼", + "bitrate": "位元率", + "no_active": "無播放活動" }, "tautulli": { - "playing": "Playing", - "transcoding": "Transcoding", - "bitrate": "Bitrate", - "no_active": "No Active Streams" + "playing": "正在播放", + "transcoding": "轉碼", + "bitrate": "位元率", + "no_active": "無播放活動" }, "jellyseerr": { - "pending": "Pending", - "approved": "Approved", - "available": "Available" + "pending": "待定", + "approved": "已接受", + "available": "可用的" }, "search": { - "placeholder": "Search…" + "placeholder": "搜尋…" }, "resources": { - "total": "Total", - "free": "Free", - "used": "Used", - "load": "Load", + "total": "全部", + "free": "剩餘", + "used": "已使用", + "load": "負載", "cpu": "CPU" }, "nzbget": { - "rate": "Rate", - "remaining": "Remaining", - "downloaded": "Downloaded" + "rate": "速率", + "remaining": "剩餘", + "downloaded": "已下載" }, "sabnzbd": { - "rate": "Rate", - "queue": "Queue", - "timeleft": "Time Left" + "rate": "速率", + "queue": "佇列", + "timeleft": "剩餘時間" }, "rutorrent": { "active": "Active", - "upload": "Upload", - "download": "Download" + "upload": "上傳", + "download": "下載" }, "radarr": { - "movies": "Movies", - "wanted": "Wanted", - "queued": "Queued", - "missing": "Missing" + "movies": "電影", + "wanted": "關注中", + "queued": "已加入佇列", + "missing": "遺失" }, "sonarr": { - "wanted": "Wanted", - "queued": "Queued", - "series": "Series" + "wanted": "關注中", + "queued": "已加入佇列", + "series": "系列" }, "readarr": { - "wanted": "Wanted", - "queued": "Queued", - "books": "Books" + "wanted": "關注中", + "queued": "已加入佇列", + "books": "書籍" }, "ombi": { - "pending": "Pending", - "approved": "Approved", - "available": "Available" + "pending": "待定", + "approved": "已接受", + "available": "可用的" }, "overseerr": { - "pending": "Pending", - "approved": "Approved", - "available": "Available", - "processing": "Processing" + "pending": "待定", + "approved": "已接受", + "available": "可用的", + "processing": "處理中" }, "pihole": { - "queries": "Queries", - "blocked": "Blocked", + "queries": "查詢數", + "blocked": "已阻擋", "gravity": "Gravity" }, "speedtest": { - "upload": "Upload", - "download": "Download", + "upload": "上行", + "download": "下行", "ping": "Ping" }, "portainer": { - "running": "Running", - "stopped": "Stopped", - "total": "Total" + "running": "運行中", + "stopped": "未運行", + "total": "全部" }, "traefik": { - "routers": "Routers", - "services": "Services", - "middleware": "Middleware" + "routers": "路由器", + "services": "服務", + "middleware": "中介軟體" }, "gotify": { - "clients": "Clients", - "apps": "Applications", - "messages": "Messages" + "clients": "客戶端", + "apps": "應用程式", + "messages": "訊息" }, "npm": { - "enabled": "Enabled", - "disabled": "Disabled", - "total": "Total" + "enabled": "已啟用", + "disabled": "已停用", + "total": "全部" }, "coinmarketcap": { - "configure": "Configure one or more crypto currencies to track", - "1hour": "1 Hour", - "1day": "1 Day", - "7days": "7 Days", - "30days": "30 Days" + "configure": "請設定一個或多個欲追蹤的加密貨幣", + "1hour": "1小時", + "1day": "1天", + "7days": "7天", + "30days": "30天" }, "prowlarr": { - "enableIndexers": "Indexers", - "numberOfGrabs": "Grabs", - "numberOfQueries": "Queries", - "numberOfFailGrabs": "Fail Grabs", - "numberOfFailQueries": "Fail Queries" + "enableIndexers": "索引器", + "numberOfGrabs": "抓取", + "numberOfQueries": "查詢數", + "numberOfFailGrabs": "抓取失敗", + "numberOfFailQueries": "查詢失敗" }, "transmission": { - "download": "Download", - "upload": "Upload", + "download": "下載", + "upload": "上傳", "leech": "Leech", "seed": "Seed" }, "jackett": { - "configured": "Configured", - "errored": "Errored" + "configured": "已配置", + "errored": "發生錯誤" }, "bazarr": { - "missingEpisodes": "Missing Episodes", - "missingMovies": "Missing Movies" + "missingEpisodes": "缺失的劇集", + "missingMovies": "缺失的電影" }, "lidarr": { - "wanted": "Wanted", - "queued": "Queued", - "albums": "Albums" + "wanted": "關注中", + "queued": "已加入佇列", + "albums": "專輯" }, "adguard": { - "queries": "Queries", - "blocked": "Blocked", - "filtered": "Filtered", - "latency": "Latency" + "queries": "查詢數", + "blocked": "已阻擋", + "filtered": "已過濾", + "latency": "延遲" }, "qbittorrent": { - "download": "Download", - "upload": "Upload", + "download": "下載", + "upload": "上傳", "leech": "Leech", "seed": "Seed" }, "mastodon": { - "user_count": "Users", - "status_count": "Posts", - "domain_count": "Domains" + "user_count": "使用者", + "status_count": "文章", + "domain_count": "網域" }, "strelaysrv": { - "numActiveSessions": "Sessions", - "numConnections": "Connections", - "dataRelayed": "Relayed", - "transferRate": "Rate" + "numActiveSessions": "工作階段", + "numConnections": "連線", + "dataRelayed": "中繼", + "transferRate": "速率" }, "authentik": { - "users": "Users", - "loginsLast24H": "Logins (24h)", - "failedLoginsLast24H": "Failed Logins (24h)" + "users": "使用者", + "loginsLast24H": "登錄(24h)", + "failedLoginsLast24H": "登錄失敗(24h)" }, "proxmox": { - "mem": "MEM", + "mem": "記憶體", "cpu": "CPU", "lxc": "LXC", "vms": "VMs" }, "unifi": { - "users": "Users", - "uptime": "System Uptime", - "days": "Days", + "users": "使用者", + "uptime": "系統運行時間", + "days": "天", "wan": "WAN", - "lan_users": "LAN Users", - "wlan_users": "WLAN Users", + "lan_users": "LAN使用者", + "wlan_users": "WLAN使用者", "up": "UP", "down": "DOWN", - "wait": "Please wait", + "wait": "請稍後", "lan": "LAN", "wlan": "WLAN", - "devices": "Devices", - "lan_devices": "LAN Devices", - "wlan_devices": "WLAN Devices" + "devices": "設備", + "lan_devices": "LAN設備", + "wlan_devices": "WLAN設備" }, "plex": { - "streams": "Active Streams", - "movies": "Movies", - "tv": "TV Shows" + "streams": "正在播放", + "movies": "電影", + "tv": "影集" }, "glances": { "cpu": "CPU", - "mem": "MEM", - "wait": "Please wait" + "mem": "記憶體", + "wait": "請稍後" }, "changedetectionio": { "totalObserved": "Total Observed", "diffsDetected": "Diffs Detected" }, "wmo": { - "0-day": "Sunny", - "0-night": "Clear", - "71-day": "Light Snow", - "71-night": "Light Snow", - "1-day": "Mainly Sunny", - "1-night": "Mainly Clear", - "2-day": "Partly Cloudy", - "2-night": "Partly Cloudy", - "3-day": "Cloudy", - "3-night": "Cloudy", - "45-day": "Foggy", - "45-night": "Foggy", - "48-day": "Foggy", - "48-night": "Foggy", - "51-day": "Light Drizzle", - "51-night": "Light Drizzle", - "53-day": "Drizzle", - "53-night": "Drizzle", - "55-day": "Heavy Drizzle", - "55-night": "Heavy Drizzle", - "56-day": "Light Freezing Drizzle", - "56-night": "Light Freezing Drizzle", - "57-day": "Freezing Drizzle", - "57-night": "Freezing Drizzle", - "61-day": "Light Rain", - "61-night": "Light Rain", - "63-day": "Rain", - "63-night": "Rain", - "65-day": "Heavy Rain", - "65-night": "Heavy Rain", - "66-day": "Freezing Rain", - "66-night": "Freezing Rain", - "67-day": "Freezing Rain", - "67-night": "Freezing Rain", - "73-day": "Snow", - "73-night": "Snow", - "75-day": "Heavy Snow", - "75-night": "Heavy Snow", - "77-day": "Snow Grains", - "77-night": "Snow Grains", - "80-day": "Light Showers", - "80-night": "Light Showers", - "81-day": "Showers", - "81-night": "Showers", - "82-day": "Heavy Showers", - "82-night": "Heavy Showers", - "85-day": "Snow Showers", - "85-night": "Snow Showers", - "86-day": "Snow Showers", - "86-night": "Snow Showers", - "95-day": "Thunderstorm", - "95-night": "Thunderstorm", - "96-day": "Thunderstorm With Hail", - "96-night": "Thunderstorm With Hail", - "99-day": "Thunderstorm With Hail", - "99-night": "Thunderstorm With Hail" + "0-day": "晴", + "0-night": "晴朗", + "71-day": "小雪", + "71-night": "小雪", + "1-day": "晴時多雲", + "1-night": "晴時多雲", + "2-day": "多雲時陰", + "2-night": "多雲時陰", + "3-day": "陰天", + "3-night": "陰天", + "45-day": "有霧", + "45-night": "有霧", + "48-day": "有霧", + "48-night": "有霧", + "51-day": "小毛雨", + "51-night": "小毛雨", + "53-day": "毛雨", + "53-night": "毛雨", + "55-day": "大毛雨", + "55-night": "大毛雨", + "56-day": "小凍毛雨", + "56-night": "小凍毛雨", + "57-day": "凍毛雨", + "57-night": "凍毛雨", + "61-day": "小雨", + "61-night": "小雨", + "63-day": "雨", + "63-night": "雨", + "65-day": "大雨", + "65-night": "大雨", + "66-day": "凍雨", + "66-night": "凍雨", + "67-day": "凍雨", + "67-night": "凍雨", + "73-day": "雪", + "73-night": "雪", + "75-day": "大雪", + "75-night": "大雪", + "77-day": "雪粒", + "77-night": "雪粒", + "80-day": "微陣雨", + "80-night": "微陣雨", + "81-day": "陣雨", + "81-night": "陣雨", + "82-day": "強陣雨", + "82-night": "強陣雨", + "85-day": "陣雪", + "85-night": "陣雪", + "86-day": "陣雪", + "86-night": "陣雪", + "95-day": "雷雨", + "95-night": "雷雨", + "96-day": "雷雨伴隨冰雹", + "96-night": "雷雨伴隨冰雹", + "99-day": "雷雨伴隨冰雹", + "99-night": "雷雨伴隨冰雹" }, "quicklaunch": { - "bookmark": "Bookmark", - "service": "Service" + "bookmark": "書籤", + "service": "服務" }, "homebridge": { - "available_update": "System", - "updates": "Updates", - "update_available": "Update Available", - "up_to_date": "Up to Date", + "available_update": "系統", + "updates": "更新", + "update_available": "有可用的更新", + "up_to_date": "已更新至最新", "child_bridges": "Child Bridges", "child_bridges_status": "{{ok}}/{{total}}" }, "autobrr": { - "approvedPushes": "Approved", - "rejectedPushes": "Rejected", - "filters": "Filters", - "indexers": "Indexers" + "approvedPushes": "接受", + "rejectedPushes": "拒絕", + "filters": "篩選器", + "indexers": "索引器" }, "watchtower": { - "containers_scanned": "Scanned", - "containers_updated": "Updated", - "containers_failed": "Failed" + "containers_scanned": "已掃描", + "containers_updated": "已更新", + "containers_failed": "失敗" }, "tubearchivist": { - "downloads": "Queue", - "videos": "Videos", - "channels": "Channels", - "playlists": "Playlists" + "downloads": "佇列", + "videos": "影片", + "channels": "頻道", + "playlists": "播放清單" }, "truenas": { - "load": "System Load", - "uptime": "Uptime", - "alerts": "Alerts", + "load": "系統負載", + "uptime": "運行時間", + "alerts": "警示", "time": "{{value, number(style: unit; unitDisplay: long;)}}" }, "navidrome": { - "nothing_streaming": "No Active Streams", - "please_wait": "Please Wait" + "nothing_streaming": "無播放活動", + "please_wait": "請稍後" }, "pyload": { - "speed": "Speed", - "active": "Active", - "queue": "Queue", - "total": "Total" + "speed": "速度", + "active": "運行中", + "queue": "佇列", + "total": "全部" }, "gluetun": { - "public_ip": "Public IP", - "region": "Region", - "country": "Country" + "public_ip": "公用IP", + "region": "地區", + "country": "國家" }, "hdhomerun": { - "channels": "Channels", - "hd": "HD" + "channels": "頻道", + "hd": "高畫質" }, "ping": { - "error": "Error", + "error": "錯誤", "ping": "Ping" }, "scrutiny": { - "passed": "Passed", - "failed": "Failed", - "unknown": "Unknown" + "passed": "通過", + "failed": "失敗", + "unknown": "未知的" }, "paperlessngx": { - "inbox": "Inbox", - "total": "Total" + "inbox": "收件箱", + "total": "全部" }, "deluge": { - "download": "Download", - "upload": "Upload", + "download": "下載", + "upload": "上傳", "leech": "Leech", "seed": "Seed" }, "flood": { - "download": "Download", - "upload": "Upload", + "download": "下載", + "upload": "上傳", "leech": "Leech", "seed": "Seed" }, "tdarr": { - "queue": "Queue", - "processed": "Processed", - "errored": "Errored", - "saved": "Saved" + "queue": "佇列", + "processed": "已處理", + "errored": "發生錯誤", + "saved": "已儲存" }, "miniflux": { - "read": "Read", - "unread": "Unread" + "read": "已讀", + "unread": "未讀" }, "nextdns": { - "wait": "Please Wait", - "no_devices": "No Device Data Received" + "wait": "請稍後", + "no_devices": "未收到裝置資料" }, "common": { "bibyterate": "{{value, rate(bits: false; binary: true)}}", "bibitrate": "{{value, rate(bits: true; binary: true)}}" }, "omada": { - "connectedAp": "Connected APs", - "activeUser": "Active devices", - "alerts": "Alerts", - "connectedGateway": "Connected gateways", - "connectedSwitches": "Connected switches" + "connectedAp": "已連接的存取點", + "activeUser": "在線裝置", + "alerts": "警示", + "connectedGateway": "已連接的閘道", + "connectedSwitches": "已連接的交換器" }, "downloadstation": { - "download": "Download", - "upload": "Upload", + "download": "下載", + "upload": "上傳", "leech": "Leech", "seed": "Seed" }, "mikrotik": { - "cpuLoad": "CPU Load", - "memoryUsed": "Memory Used", - "uptime": "Uptime", - "numberOfLeases": "Leases" + "cpuLoad": "CPU負載", + "memoryUsed": "已使用的記憶體", + "uptime": "運行時間", + "numberOfLeases": "租約" }, "xteve": { - "streams_all": "All Streams", - "streams_active": "Active Streams", - "streams_xepg": "XEPG Channels" + "streams_all": "所有播放活動", + "streams_active": "正在播放", + "streams_xepg": "XEPG頻道" }, "opnsense": { - "cpu": "CPU Load", + "cpu": "CPU負載", "memory": "Active Memory", - "wanUpload": "WAN Upload", - "wanDownload": "WAN Download" + "wanUpload": "WAN上傳", + "wanDownload": "WAN下載" + }, + "moonraker": { + "printer_state": "Printer State", + "print_status": "Print Status", + "print_progress": "Progress", + "layers": "Layers" } } diff --git a/src/components/bookmarks/item.jsx b/src/components/bookmarks/item.jsx index 17fcbe9e..89dd698d 100644 --- a/src/components/bookmarks/item.jsx +++ b/src/components/bookmarks/item.jsx @@ -19,7 +19,7 @@ export default function Item({ bookmark }) {
{bookmark.icon &&
- +
} {!bookmark.icon && bookmark.abbr} diff --git a/src/components/resolvedicon.jsx b/src/components/resolvedicon.jsx index 3dad2b0e..8657a754 100644 --- a/src/components/resolvedicon.jsx +++ b/src/components/resolvedicon.jsx @@ -1,9 +1,9 @@ import Image from "next/future/image"; -export default function ResolvedIcon({ icon, width = 32, height = 32 }) { +export default function ResolvedIcon({ icon, width = 32, height = 32, alt = "logo" }) { // direct or relative URLs if (icon.startsWith("http") || icon.startsWith("/")) { - return logo; + return {alt}; } // mdi- prefixed, material design icons @@ -31,7 +31,7 @@ export default function ResolvedIcon({ icon, width = 32, height = 32 }) { src={`https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${iconName}.png`} width={width} height={height} - alt="logo" + alt={alt} /> ); -} \ No newline at end of file +} diff --git a/src/pages/index.jsx b/src/pages/index.jsx index e891e2f9..b6a1881a 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -99,7 +99,7 @@ function Index({ initialSettings, fallback }) { localStorage.setItem("hash", hashData.hash); } - if (previousHash && previousHash !== hashData.hash) { + if (!initialSettings.isValid || (previousHash && previousHash !== hashData.hash)) { setStale(true); localStorage.setItem("hash", hashData.hash); @@ -111,7 +111,7 @@ function Index({ initialSettings, fallback }) { } } } - }, [hashData]); + }, [hashData, initialSettings]); if (stale) { return ( diff --git a/src/utils/config/config.js b/src/utils/config/config.js index 57a63f7b..0415cfca 100644 --- a/src/utils/config/config.js +++ b/src/utils/config/config.js @@ -1,6 +1,6 @@ /* eslint-disable no-console */ import { join } from "path"; -import { existsSync, copyFile, readFileSync } from "fs"; +import { existsSync, copyFile, readFileSync, statSync } from "fs"; import yaml from "js-yaml"; @@ -32,5 +32,18 @@ export function getSettings() { const settingsYaml = join(process.cwd(), "config", "settings.yaml"); const fileContents = readFileSync(settingsYaml, "utf8"); - return yaml.load(fileContents) ?? {}; + + let stats; + try { + stats = statSync(settingsYaml); + } catch (e) { + stats = {}; + } + + const yamlLoaded = yaml.load(fileContents) ?? {}; + + return { + ...yamlLoaded, + isValid: fileContents !== "-\n" && stats.size !== 2 // see https://github.com/benphelps/homepage/pull/609 + }; } \ No newline at end of file diff --git a/src/utils/proxy/handlers/credentialed.js b/src/utils/proxy/handlers/credentialed.js index 5d34264d..10ca2b9c 100644 --- a/src/utils/proxy/handlers/credentialed.js +++ b/src/utils/proxy/handlers/credentialed.js @@ -30,6 +30,8 @@ export default async function credentialedProxyHandler(req, res, map) { headers["X-gotify-Key"] = `${widget.key}`; } else if (widget.type === "authentik") { headers.Authorization = `Bearer ${widget.key}`; + } else if (widget.type === "truenas") { + headers.Authorization = `Bearer ${widget.key}`; } else if (widget.type === "proxmox") { headers.Authorization = `PVEAPIToken=${widget.username}=${widget.password}`; } else if (widget.type === "autobrr") { diff --git a/src/utils/proxy/use-widget-api.js b/src/utils/proxy/use-widget-api.js index 05eb220e..f9235d78 100644 --- a/src/utils/proxy/use-widget-api.js +++ b/src/utils/proxy/use-widget-api.js @@ -4,8 +4,8 @@ import { formatProxyUrl } from "./api-helpers"; export default function useWidgetAPI(widget, ...options) { const config = {}; - if (options?.refreshInterval) { - config.refreshInterval = options.refreshInterval; + if (options && options[1]?.refreshInterval) { + config.refreshInterval = options[1].refreshInterval; } const { data, error } = useSWR(formatProxyUrl(widget, ...options), config); // make the data error the top-level error diff --git a/src/widgets/components.js b/src/widgets/components.js index 10277732..d9240460 100644 --- a/src/widgets/components.js +++ b/src/widgets/components.js @@ -23,6 +23,7 @@ const components = { mastodon: dynamic(() => import("./mastodon/component")), miniflux: dynamic(() => import("./miniflux/component")), mikrotik: dynamic(() => import("./mikrotik/component")), + moonraker: dynamic(() => import("./moonraker/component")), navidrome: dynamic(() => import("./navidrome/component")), nextdns: dynamic(() => import("./nextdns/component")), npm: dynamic(() => import("./npm/component")), diff --git a/src/widgets/moonraker/component.jsx b/src/widgets/moonraker/component.jsx new file mode 100644 index 00000000..81bf8b90 --- /dev/null +++ b/src/widgets/moonraker/component.jsx @@ -0,0 +1,51 @@ +import { useTranslation } from "next-i18next"; + +import Container from "components/services/widget/container"; +import Block from "components/services/widget/block"; +import useWidgetAPI from "utils/proxy/use-widget-api"; + +export default function Component({ service }) { + const { t } = useTranslation(); + + const { widget } = service; + + const { data: printStats, error: printStatsError } = useWidgetAPI(widget, "print_stats"); + const { data: displayStatus, error: displayStatsError } = useWidgetAPI(widget, "display_status"); + const { data: webHooks, error: webHooksError } = useWidgetAPI(widget, "webhooks"); + + if (printStatsError || displayStatsError || webHooksError) { + const finalError = printStatsError ?? displayStatsError ?? webHooksError; + return ; + } + + if (!printStats || !displayStatus || !webHooks) { + return ( + + + + ); + } + + if (webHooks.result.status.webhooks.state === "shutdown") { + return ( + + + + ); + } + + let currentLayer = "-"; + let totalLayer = "-"; + if (printStats.result.status.print_stats.info.total_layer !== null) { + currentLayer = printStats.result.status.print_stats.info.current_layer; + totalLayer = printStats.result.status.print_stats.info.total_layer; + } + + return ( + + + + + + ); +} diff --git a/src/widgets/moonraker/widget.js b/src/widgets/moonraker/widget.js new file mode 100644 index 00000000..314a8670 --- /dev/null +++ b/src/widgets/moonraker/widget.js @@ -0,0 +1,20 @@ +import genericProxyHandler from "utils/proxy/handlers/generic"; + +const widget = { + api: "{url}/printer/objects/query?{endpoint}", + proxyHandler: genericProxyHandler, + + mappings: { + print_stats: { + endpoint: "print_stats", + }, + display_status: { + endpoint: "display_status", + }, + webhooks: { + endpoint: "webhooks", + }, + }, +}; + +export default widget; diff --git a/src/widgets/truenas/widget.js b/src/widgets/truenas/widget.js index 4c479e9f..3139019b 100644 --- a/src/widgets/truenas/widget.js +++ b/src/widgets/truenas/widget.js @@ -1,9 +1,31 @@ import { jsonArrayFilter } from "utils/proxy/api-helpers"; +import credentialedProxyHandler from "utils/proxy/handlers/credentialed"; import genericProxyHandler from "utils/proxy/handlers/generic"; +import getServiceWidget from "utils/config/service-helpers"; const widget = { api: "{url}/api/v2.0/{endpoint}", - proxyHandler: genericProxyHandler, + proxyHandler: async (req, res, map) => { // choose proxy handler based on widget settings + const { group, service } = req.query; + + if (group && service) { + const widgetOpts = await getServiceWidget(group, service); + let handler; + if (widgetOpts.username && widgetOpts.password) { + handler = genericProxyHandler; + } else if (widgetOpts.key) { + handler = credentialedProxyHandler; + } + + if (handler) { + return handler(req, res, map); + } + + return res.status(500).json({ error: "Username / password or API key required" }); + } + + return res.status(500).json({ error: "Error parsing widget request" }); + }, mappings: { alerts: { diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js index 3e73e55f..091a616f 100644 --- a/src/widgets/widgets.js +++ b/src/widgets/widgets.js @@ -18,6 +18,7 @@ import lidarr from "./lidarr/widget"; import mastodon from "./mastodon/widget"; import miniflux from "./miniflux/widget"; import mikrotik from "./mikrotik/widget"; +import moonraker from "./moonraker/widget"; import navidrome from "./navidrome/widget"; import nextdns from "./nextdns/widget"; import npm from "./npm/widget"; @@ -75,6 +76,7 @@ const widgets = { mastodon, miniflux, mikrotik, + moonraker, navidrome, nextdns, npm,