diff --git a/.dockerignore b/.dockerignore
index edbf85256..90fbe1655 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -16,6 +16,7 @@
**/compose*
**/Dockerfile*
**/node_modules
+!**/node_modules/.pnpm/compressjs@*/**
**/npm-debug.log
**/obj
**/secrets.dev.yaml
diff --git a/Dockerfile b/Dockerfile
index 9fec35e70..f8bb15517 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -52,6 +52,8 @@ COPY --link --chown=1000:1000 /public ./public/
# Copy files from builder
COPY --link --from=builder --chown=1000:1000 /app/.next/standalone ./
COPY --link --from=builder --chown=1000:1000 /app/.next/static/ ./.next/static/
+# see https://github.com/benphelps/homepage/issues/1795
+COPY --link --from=builder /app/node_modules/.pnpm/compressjs@1.0.3/node_modules/compressjs/lib/ ./node_modules/.pnpm/compressjs@1.0.3/node_modules/compressjs/lib/
COPY --link --chmod=755 docker-entrypoint.sh /usr/local/bin/
RUN apk add --no-cache su-exec
diff --git a/public/locales/ar/common.json b/public/locales/ar/common.json
index e4eb1d0c5..d48111dac 100644
--- a/public/locales/ar/common.json
+++ b/public/locales/ar/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/bg/common.json b/public/locales/bg/common.json
index dc3bbd036..cddb65636 100644
--- a/public/locales/bg/common.json
+++ b/public/locales/bg/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/ca/common.json b/public/locales/ca/common.json
index 5147a31dd..fdf0ead38 100644
--- a/public/locales/ca/common.json
+++ b/public/locales/ca/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"totalUsed": "Used Storage",
"noRecent": "Out of Date"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/cs/common.json b/public/locales/cs/common.json
index 39d60f58d..2ead9c2fa 100644
--- a/public/locales/cs/common.json
+++ b/public/locales/cs/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/da/common.json b/public/locales/da/common.json
index 8b1ded637..5382ad049 100644
--- a/public/locales/da/common.json
+++ b/public/locales/da/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/de/common.json b/public/locales/de/common.json
index 9df3bfd2a..016b0289a 100644
--- a/public/locales/de/common.json
+++ b/public/locales/de/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/el/common.json b/public/locales/el/common.json
index 49b76d490..6cf37721c 100644
--- a/public/locales/el/common.json
+++ b/public/locales/el/common.json
@@ -237,17 +237,17 @@
},
"bazarr": {
"missingEpisodes": "Επεισόδια που λείπουν",
- "missingMovies": "Missing Movies"
+ "missingMovies": "Ταινίες που Λείπουν"
},
"ombi": {
- "pending": "Pending",
- "approved": "Approved",
- "available": "Available"
+ "pending": "Σε εκκρεμότητα",
+ "approved": "Εγκρίθηκε",
+ "available": "Διαθέσιμο"
},
"jellyseerr": {
- "pending": "Pending",
- "approved": "Approved",
- "available": "Available"
+ "pending": "Σε εκκρεμότητα",
+ "approved": "Εγκρίθηκε",
+ "available": "Διαθέσιμο"
},
"overseerr": {
"pending": "Pending",
@@ -257,25 +257,25 @@
},
"pihole": {
"queries": "Queries",
- "blocked": "Blocked",
+ "blocked": "Αποκλεισμένο",
"gravity": "Gravity",
- "blocked_percent": "Blocked %"
+ "blocked_percent": "Αποκλεισμένο %"
},
"adguard": {
- "queries": "Queries",
- "blocked": "Blocked",
- "filtered": "Filtered",
- "latency": "Latency"
+ "queries": "Αναζητήσεις",
+ "blocked": "Αποκλεισμένο",
+ "filtered": "Φιλτραρισμένα",
+ "latency": "Καθυστέρηση"
},
"speedtest": {
- "upload": "Upload",
- "download": "Download",
+ "upload": "Ανέβασμα",
+ "download": "Λήψη",
"ping": "Ping"
},
"portainer": {
"running": "Running",
- "stopped": "Stopped",
- "total": "Total"
+ "stopped": "Σταματημένο",
+ "total": "Σύνολο"
},
"tdarr": {
"queue": "Queue",
@@ -563,7 +563,7 @@
"records_total": "Queue Length"
},
"pterodactyl": {
- "servers": "Servers",
+ "servers": "Διακομιστές",
"nodes": "Nodes"
},
"prometheus": {
@@ -595,22 +595,22 @@
"shows": "Εκπομπές",
"recordings": "Εγγραφές",
"scheduled": "Προγραμματισμένα",
- "passes": "Passes"
+ "passes": "Περάσματα"
},
"whatsupdocker": {
"monitoring": "Monitoring",
"updates": "Updates"
},
"tailscale": {
- "address": "Address",
- "never": "Never",
+ "address": "Διεύθυνση",
+ "never": "Ποτέ",
"years": "{{number}}y",
"weeks": "{{number}}w",
"days": "{{number}}d",
"hours": "{{number}}h",
- "expires": "Expires",
+ "expires": "Λήγει",
"last_seen": "Last Seen",
- "now": "Now",
+ "now": "Τώρα",
"minutes": "{{number}}m",
"seconds": "{{number}}s",
"ago": "{{value}} Ago"
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 7ff2973c0..2cf3f1bae 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -703,9 +703,17 @@
"totalUsed": "Used Storage"
},
"mealie": {
- "recipes": "Recipes",
- "users": "Users",
- "categories": "Categories",
- "tags": "Tags"
+ "recipes": "Recipes",
+ "users": "Users",
+ "categories": "Categories",
+ "tags": "Tags"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/eo/common.json b/public/locales/eo/common.json
index 019d05b73..5539ebc23 100644
--- a/public/locales/eo/common.json
+++ b/public/locales/eo/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/es/common.json b/public/locales/es/common.json
index 37d4b0e6b..f70bd011a 100644
--- a/public/locales/es/common.json
+++ b/public/locales/es/common.json
@@ -692,5 +692,13 @@
"errored": "Errores",
"noRecent": "Caducado",
"totalUsed": "Almacenamiento usado"
+ },
+ "openmediavault": {
+ "running": "Ejecutando",
+ "downloading": "Descargando",
+ "total": "Total",
+ "stopped": "Detenido",
+ "passed": "Aprobado",
+ "failed": "Fallido"
}
}
diff --git a/public/locales/eu/common.json b/public/locales/eu/common.json
index 8ba75c93a..9737a2416 100644
--- a/public/locales/eu/common.json
+++ b/public/locales/eu/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/fi/common.json b/public/locales/fi/common.json
index 505c7a60c..6017b1794 100644
--- a/public/locales/fi/common.json
+++ b/public/locales/fi/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/fr/common.json b/public/locales/fr/common.json
index 8d76d83f4..8b0859469 100644
--- a/public/locales/fr/common.json
+++ b/public/locales/fr/common.json
@@ -692,5 +692,13 @@
"errored": "Erreur",
"noRecent": "Obsolète",
"totalUsed": "Esp. Utilisé"
+ },
+ "openmediavault": {
+ "downloading": "Téléchargement",
+ "total": "Total",
+ "running": "Démarré",
+ "stopped": "Arrêté",
+ "passed": "Réussi",
+ "failed": "Échoué"
}
}
diff --git a/public/locales/he/common.json b/public/locales/he/common.json
index fb335e708..7af222f25 100644
--- a/public/locales/he/common.json
+++ b/public/locales/he/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/hi/common.json b/public/locales/hi/common.json
index bd2491e10..6754bad7e 100644
--- a/public/locales/hi/common.json
+++ b/public/locales/hi/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/hr/common.json b/public/locales/hr/common.json
index 26772c071..299c406c0 100644
--- a/public/locales/hr/common.json
+++ b/public/locales/hr/common.json
@@ -692,5 +692,13 @@
"totalUsed": "Used Storage",
"ok": "Ok",
"errored": "Errors"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/hu/common.json b/public/locales/hu/common.json
index a8b2b9e8f..2a3f1a8a2 100644
--- a/public/locales/hu/common.json
+++ b/public/locales/hu/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/id/common.json b/public/locales/id/common.json
index e0a8e542b..f66f61112 100644
--- a/public/locales/id/common.json
+++ b/public/locales/id/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/it/common.json b/public/locales/it/common.json
index 502116983..d1a1b85ff 100644
--- a/public/locales/it/common.json
+++ b/public/locales/it/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "total": "Total",
+ "running": "Running",
+ "downloading": "Downloading",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/ja/common.json b/public/locales/ja/common.json
index 089ad81b6..34be82e38 100644
--- a/public/locales/ja/common.json
+++ b/public/locales/ja/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/ko/common.json b/public/locales/ko/common.json
index 31f4a6ff5..666fc35ec 100644
--- a/public/locales/ko/common.json
+++ b/public/locales/ko/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/lv/common.json b/public/locales/lv/common.json
index 2bdd7a4ce..9ddb8e4cf 100644
--- a/public/locales/lv/common.json
+++ b/public/locales/lv/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/ms/common.json b/public/locales/ms/common.json
index 5c5ecff98..c1f79f526 100644
--- a/public/locales/ms/common.json
+++ b/public/locales/ms/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "downloading": "Downloading",
+ "total": "Total",
+ "failed": "Failed"
}
}
diff --git a/public/locales/nb-NO/common.json b/public/locales/nb-NO/common.json
index dea49c31c..738354e2e 100644
--- a/public/locales/nb-NO/common.json
+++ b/public/locales/nb-NO/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/nl/common.json b/public/locales/nl/common.json
index 39fe9fe5f..4521e9351 100644
--- a/public/locales/nl/common.json
+++ b/public/locales/nl/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/pl/common.json b/public/locales/pl/common.json
index 765b48f61..17320906e 100644
--- a/public/locales/pl/common.json
+++ b/public/locales/pl/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/pt-BR/common.json b/public/locales/pt-BR/common.json
index 13735c01c..00c51552d 100644
--- a/public/locales/pt-BR/common.json
+++ b/public/locales/pt-BR/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/pt/common.json b/public/locales/pt/common.json
index 4c7f42595..8429db914 100644
--- a/public/locales/pt/common.json
+++ b/public/locales/pt/common.json
@@ -701,5 +701,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/ro/common.json b/public/locales/ro/common.json
index 567bfba5f..b72cf9ad7 100644
--- a/public/locales/ro/common.json
+++ b/public/locales/ro/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/ru/common.json b/public/locales/ru/common.json
index 5c65900ce..502adeaf8 100644
--- a/public/locales/ru/common.json
+++ b/public/locales/ru/common.json
@@ -16,11 +16,11 @@
"free": "Свободно",
"used": "Использовано",
"load": "Загрузка",
- "cpu": "Процессор",
- "mem": "Память",
- "temp": "Температура",
+ "cpu": "ЦП",
+ "mem": "ОЗУ",
+ "temp": "Темпер.",
"max": "Макс.",
- "uptime": "UP",
+ "uptime": "Работает",
"months": "мес",
"days": "д",
"hours": "ч",
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "passed": "Passed",
+ "failed": "Failed",
+ "running": "Running",
+ "stopped": "Stopped"
}
}
diff --git a/public/locales/sk/common.json b/public/locales/sk/common.json
index aba3bc6fa..c666bc7a4 100644
--- a/public/locales/sk/common.json
+++ b/public/locales/sk/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "total": "Total",
+ "downloading": "Downloading",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/sl/common.json b/public/locales/sl/common.json
index 51e78672a..12623c6f3 100644
--- a/public/locales/sl/common.json
+++ b/public/locales/sl/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/sr/common.json b/public/locales/sr/common.json
index 78075f4cb..437108fd5 100644
--- a/public/locales/sr/common.json
+++ b/public/locales/sr/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed",
+ "downloading": "Downloading"
}
}
diff --git a/public/locales/sv/common.json b/public/locales/sv/common.json
index 1dd59ece7..8d24f5c96 100644
--- a/public/locales/sv/common.json
+++ b/public/locales/sv/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/te/common.json b/public/locales/te/common.json
index 77cc7df01..6c37a87c6 100644
--- a/public/locales/te/common.json
+++ b/public/locales/te/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "failed": "Failed",
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed"
}
}
diff --git a/public/locales/th/common.json b/public/locales/th/common.json
index cf54e20ce..c3cbc7285 100644
--- a/public/locales/th/common.json
+++ b/public/locales/th/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/tr/common.json b/public/locales/tr/common.json
index 8dc340b25..7349981b4 100644
--- a/public/locales/tr/common.json
+++ b/public/locales/tr/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/uk/common.json b/public/locales/uk/common.json
index c94e7e5ba..71d754983 100644
--- a/public/locales/uk/common.json
+++ b/public/locales/uk/common.json
@@ -670,9 +670,9 @@
"maxPlayers": "Максимум гравців",
"bots": "Ботів",
"ping": "Пінг",
- "status": "Status",
- "online": "Online",
- "offline": "Offline"
+ "status": "Статус",
+ "online": "В мережі",
+ "offline": "Не в мережі"
},
"azuredevops": {
"result": "Результат",
@@ -692,5 +692,13 @@
"errored": "Помилки",
"noRecent": "Застарілий",
"totalUsed": "Використовувана пам'ять"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/vi/common.json b/public/locales/vi/common.json
index cf2d03f8e..ebc0eae3e 100644
--- a/public/locales/vi/common.json
+++ b/public/locales/vi/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/yue/common.json b/public/locales/yue/common.json
index 8c6028534..0dc4ee45e 100644
--- a/public/locales/yue/common.json
+++ b/public/locales/yue/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/zh-CN/common.json b/public/locales/zh-CN/common.json
index 2512b2566..2d2a76cf8 100644
--- a/public/locales/zh-CN/common.json
+++ b/public/locales/zh-CN/common.json
@@ -692,5 +692,13 @@
"errored": "Errors",
"noRecent": "Out of Date",
"totalUsed": "Used Storage"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/public/locales/zh-Hant/common.json b/public/locales/zh-Hant/common.json
index f8969c29c..c499b6351 100644
--- a/public/locales/zh-Hant/common.json
+++ b/public/locales/zh-Hant/common.json
@@ -692,5 +692,13 @@
"errored": "錯誤",
"noRecent": "已過時",
"totalUsed": "已使用空間"
+ },
+ "openmediavault": {
+ "downloading": "Downloading",
+ "total": "Total",
+ "running": "Running",
+ "stopped": "Stopped",
+ "passed": "Passed",
+ "failed": "Failed"
}
}
diff --git a/src/utils/config/service-helpers.js b/src/utils/config/service-helpers.js
index 83b4b07bc..4488277ad 100644
--- a/src/utils/config/service-helpers.js
+++ b/src/utils/config/service-helpers.js
@@ -298,6 +298,7 @@ export function cleanServiceGroups(groups) {
metric, // glances
stream, // mjpeg
fit,
+ method, // openmediavault widget
} = cleanedService.widget;
let fieldsList = fields;
@@ -368,6 +369,9 @@ export function cleanServiceGroups(groups) {
if (stream) cleanedService.widget.stream = stream;
if (fit) cleanedService.widget.fit = fit;
}
+ if (type === "openmediavault") {
+ if (method) cleanedService.widget.method = method;
+ }
}
return cleanedService;
diff --git a/src/widgets/components.js b/src/widgets/components.js
index 5cb23bba3..f3242ce4b 100644
--- a/src/widgets/components.js
+++ b/src/widgets/components.js
@@ -60,6 +60,7 @@ const components = {
ombi: dynamic(() => import("./ombi/component")),
opnsense: dynamic(() => import("./opnsense/component")),
overseerr: dynamic(() => import("./overseerr/component")),
+ openmediavault: dynamic(() => import("./openmediavault/component")),
paperlessngx: dynamic(() => import("./paperlessngx/component")),
pfsense: dynamic(() => import("./pfsense/component")),
photoprism: dynamic(() => import("./photoprism/component")),
diff --git a/src/widgets/openmediavault/component.jsx b/src/widgets/openmediavault/component.jsx
new file mode 100644
index 000000000..bd34a7502
--- /dev/null
+++ b/src/widgets/openmediavault/component.jsx
@@ -0,0 +1,16 @@
+import ServicesGetStatus from "./methods/services_get_status";
+import SmartGetList from "./methods/smart_get_list";
+import DownloaderGetDownloadList from "./methods/downloader_get_downloadlist";
+
+export default function Component({ service }) {
+ switch (service.widget.method) {
+ case "services.getStatus":
+ return ;
+ case "smart.getListBg":
+ return ;
+ case "downloader.getDownloadList":
+ return ;
+ default:
+ return null;
+ }
+}
diff --git a/src/widgets/openmediavault/methods/downloader_get_downloadlist.jsx b/src/widgets/openmediavault/methods/downloader_get_downloadlist.jsx
new file mode 100644
index 000000000..ed776db0f
--- /dev/null
+++ b/src/widgets/openmediavault/methods/downloader_get_downloadlist.jsx
@@ -0,0 +1,36 @@
+import useWidgetAPI from "utils/proxy/use-widget-api";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
+
+const downloadReduce = (acc, e) => {
+ if (e.downloading) {
+ return acc + 1;
+ }
+ return acc;
+};
+
+const items = [
+ { label: "openmediavault.downloading", getNumber: (data) => (!data ? null : data.reduce(downloadReduce, 0)) },
+ { label: "openmediavault.total", getNumber: (data) => (!data ? null : data?.length) },
+];
+
+export default function Component({ service }) {
+ const { data, error } = useWidgetAPI(service.widget);
+
+ if (error) {
+ return ;
+ }
+
+ const itemsWithData = items.map((item) => ({
+ ...item,
+ number: item.getNumber(data?.response?.data),
+ }));
+
+ return (
+
+ {itemsWithData.map((e) => (
+
+ ))}
+
+ );
+}
diff --git a/src/widgets/openmediavault/methods/services_get_status.jsx b/src/widgets/openmediavault/methods/services_get_status.jsx
new file mode 100644
index 000000000..3ec66a45c
--- /dev/null
+++ b/src/widgets/openmediavault/methods/services_get_status.jsx
@@ -0,0 +1,43 @@
+import useWidgetAPI from "utils/proxy/use-widget-api";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
+
+const isRunningReduce = (acc, e) => {
+ if (e.running) {
+ return acc + 1;
+ }
+ return acc;
+};
+const notRunningReduce = (acc, e) => {
+ if (!e.running) {
+ return acc + 1;
+ }
+ return acc;
+};
+
+const items = [
+ { label: "openmediavault.running", getNumber: (data) => (!data ? null : data.reduce(isRunningReduce, 0)) },
+ { label: "openmediavault.stopped", getNumber: (data) => (!data ? null : data.reduce(notRunningReduce, 0)) },
+ { label: "openmediavault.total", getNumber: (data) => (!data ? null : data?.length) },
+];
+
+export default function Component({ service }) {
+ const { data, error } = useWidgetAPI(service.widget);
+
+ if (error) {
+ return ;
+ }
+
+ const itemsWithData = items.map((item) => ({
+ ...item,
+ number: item.getNumber(data?.response?.data),
+ }));
+
+ return (
+
+ {itemsWithData.map((e) => (
+
+ ))}
+
+ );
+}
diff --git a/src/widgets/openmediavault/methods/smart_get_list.jsx b/src/widgets/openmediavault/methods/smart_get_list.jsx
new file mode 100644
index 000000000..55a76db67
--- /dev/null
+++ b/src/widgets/openmediavault/methods/smart_get_list.jsx
@@ -0,0 +1,42 @@
+import useWidgetAPI from "utils/proxy/use-widget-api";
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
+
+const passedReduce = (acc, e) => {
+ if (e.overallstatus === "GOOD") {
+ return acc + 1;
+ }
+ return acc;
+};
+const failedReduce = (acc, e) => {
+ if (e.overallstatus !== "GOOD") {
+ return acc + 1;
+ }
+ return acc;
+};
+
+const items = [
+ { label: "openmediavault.passed", getNumber: (data) => (!data ? null : data.reduce(passedReduce, 0)) },
+ { label: "openmediavault.failed", getNumber: (data) => (!data ? null : data.reduce(failedReduce, 0)) },
+];
+
+export default function Component({ service }) {
+ const { data, error } = useWidgetAPI(service.widget);
+
+ if (error) {
+ return ;
+ }
+
+ const itemsWithData = items.map((item) => ({
+ ...item,
+ number: item.getNumber(JSON.parse(data?.response?.output || "{}")?.data),
+ }));
+
+ return (
+
+ {itemsWithData.map((e) => (
+
+ ))}
+
+ );
+}
diff --git a/src/widgets/openmediavault/proxy.js b/src/widgets/openmediavault/proxy.js
new file mode 100644
index 000000000..a9099d244
--- /dev/null
+++ b/src/widgets/openmediavault/proxy.js
@@ -0,0 +1,151 @@
+import { formatApiCall } from "utils/proxy/api-helpers";
+import { httpProxy } from "utils/proxy/http";
+import getServiceWidget from "utils/config/service-helpers";
+import { addCookieToJar, setCookieHeader } from "utils/proxy/cookie-jar";
+import createLogger from "utils/logger";
+import widgets from "widgets/widgets";
+
+const PROXY_NAME = "OMVProxyHandler";
+const BG_MAX_RETRIES = 50;
+const BG_POLL_PERIOD = 500;
+
+const logger = createLogger(PROXY_NAME);
+
+async function getWidget(req) {
+ const { group, service } = req.query;
+
+ if (!group || !service) {
+ logger.debug("Invalid or missing service '%s' or group '%s'", service, group);
+ return null;
+ }
+
+ const widget = await getServiceWidget(group, service);
+
+ if (!widget) {
+ logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group);
+ return null;
+ }
+
+ return widget;
+}
+
+async function rpc(url, request) {
+ const params = {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(request),
+ };
+ setCookieHeader(url, params);
+ const [status, contentType, data, headers] = await httpProxy(url, params);
+
+ return { status, contentType, data, headers };
+}
+
+async function poll(attemptsLeft, makeReqByPos, pos = 0) {
+ if (attemptsLeft <= 0) {
+ return null;
+ }
+
+ const resp = await makeReqByPos(pos);
+
+ const data = JSON.parse(resp.data.toString()).response;
+ if (data.running === true || data.outputPending) {
+ await new Promise((resolve) => {
+ setTimeout(resolve, BG_POLL_PERIOD);
+ });
+ return poll(attemptsLeft - 1, makeReqByPos, data.pos);
+ }
+ return resp;
+}
+
+async function tryLogin(widget) {
+ const url = new URL(formatApiCall(widgets?.[widget.type]?.api, { ...widget }));
+ const { username, password } = widget;
+ const resp = await rpc(url, {
+ method: "login",
+ service: "session",
+ params: { username, password },
+ });
+
+ if (resp.status !== 200) {
+ logger.error("HTTP %d logging in to OpenMediaVault. Data: %s", resp.status, resp.data);
+ return [false, resp];
+ }
+
+ const json = JSON.parse(resp.data.toString());
+ if (json.response.authenticated !== true) {
+ logger.error("Login error in OpenMediaVault. Data: %s", resp.data);
+ resp.status = 401;
+ return [false, resp];
+ }
+
+ return [true, resp];
+}
+async function processBg(url, filename) {
+ const resp = await poll(BG_MAX_RETRIES, (pos) =>
+ rpc(url, {
+ service: "exec",
+ method: "getOutput",
+ params: { pos, filename },
+ })
+ );
+
+ if (resp == null) {
+ const errText = "The maximum number of attempts to receive a response from Bg data has been exceeded.";
+ logger.error(errText);
+ return errText;
+ }
+ if (resp.status !== 200) {
+ logger.error("HTTP %d getting Bg data from OpenMediaVault RPC. Data: %s", resp.status, resp.data);
+ }
+ return resp;
+}
+
+export default async function proxyHandler(req, res) {
+ const widget = await getWidget(req);
+ if (!widget) {
+ return res.status(400).json({ error: "Invalid proxy service type" });
+ }
+
+ const api = widgets?.[widget.type]?.api;
+ if (!api) {
+ return res.status(403).json({ error: "Service does not support RPC calls" });
+ }
+
+ const url = new URL(formatApiCall(api, { ...widget }));
+ const [service, method] = widget.method.split(".");
+ const rpcReq = { params: { limit: -1, start: 0 }, service, method };
+
+ let resp = await rpc(url, rpcReq);
+
+ if (resp.status === 401) {
+ logger.debug("Session not authenticated.");
+ const [success, lResp] = await tryLogin(widget);
+
+ if (success) {
+ addCookieToJar(url, lResp.headers);
+ } else {
+ res.status(lResp.status).json({ error: { message: `HTTP Error ${lResp.status}`, url, data: lResp.data } });
+ }
+
+ logger.debug("Retrying OpenMediaVault request after login.");
+ resp = await rpc(url, rpcReq);
+ }
+
+ if (resp.status !== 200) {
+ logger.error("HTTP %d getting data from OpenMediaVault RPC. Data: %s", resp.status, resp.data);
+ return res.status(resp.status).json({ error: { message: `HTTP Error ${resp.status}`, url, data: resp.data } });
+ }
+
+ if (method.endsWith("Bg")) {
+ const json = JSON.parse(resp.data.toString());
+ const bgResp = await processBg(url, json.response);
+
+ if (typeof bgResp === "string") {
+ return res.status(400).json({ error: bgResp });
+ }
+ return res.status(bgResp.status).send(bgResp.data);
+ }
+
+ return res.status(resp.status).send(resp.data);
+}
diff --git a/src/widgets/openmediavault/widget.js b/src/widgets/openmediavault/widget.js
new file mode 100644
index 000000000..3678ebe8e
--- /dev/null
+++ b/src/widgets/openmediavault/widget.js
@@ -0,0 +1,8 @@
+import proxyHandler from "./proxy";
+
+const widget = {
+ api: "{url}/rpc.php",
+ proxyHandler,
+};
+
+export default widget;
diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js
index 11e1b9faa..1b7d9f1b6 100644
--- a/src/widgets/widgets.js
+++ b/src/widgets/widgets.js
@@ -54,6 +54,7 @@ import omada from "./omada/widget";
import ombi from "./ombi/widget";
import opnsense from "./opnsense/widget";
import overseerr from "./overseerr/widget";
+import openmediavault from "./openmediavault/widget";
import paperlessngx from "./paperlessngx/widget";
import pfsense from "./pfsense/widget";
import photoprism from "./photoprism/widget";
@@ -150,6 +151,7 @@ const widgets = {
ombi,
opnsense,
overseerr,
+ openmediavault,
paperlessngx,
pfsense,
photoprism,