diff --git a/README.md b/README.md
index 2eeb6dc9..1630d1ba 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,7 @@
- Images built for AMD64 (x86_64), ARM64, ARMv7 and ARMv6
- Supports all Raspberry Pi's, most SBCs & Apple Silicon
- Full i18n support with automatic language detection
- - Translantions for Catalan, Chinese, Dutch, Finnish, French, German, Hebrew, Hungarian, Norwegian Bokmål, Polish, Portuguese, Portuguese (Brazil), Romainian, Russian, Spanish, Swedish and Yue
+ - Translations for Catalan, Chinese, Dutch, Finnish, French, German, Hebrew, Hungarian, Norwegian Bokmål, Polish, Portuguese, Portuguese (Brazil), Romainian, Russian, Spanish, Swedish and Yue
- Want to help translate? [Join the Weblate project](https://hosted.weblate.org/engage/homepage/)
- Service & Web Bookmarks
- Docker Integration
diff --git a/public/locales/de/common.json b/public/locales/de/common.json
index 3dbe8beb..4512ad7b 100644
--- a/public/locales/de/common.json
+++ b/public/locales/de/common.json
@@ -11,7 +11,7 @@
"total": "Gesamt",
"free": "Frei",
"used": "Gebraucht",
- "load": "Belastung",
+ "load": "Last",
"cpu": "CPU"
},
"docker": {
@@ -25,13 +25,13 @@
"playing": "Spielen",
"transcoding": "Transcodierung",
"bitrate": "Bitrate",
- "no_active": "Keine aktive Streams"
+ "no_active": "Keine aktiven Streams"
},
"tautulli": {
"playing": "Spielen",
"transcoding": "Transcodierung",
"bitrate": "Bitrate",
- "no_active": "Keine aktiven streamen"
+ "no_active": "Keine aktiven Streams"
},
"rutorrent": {
"active": "Aktiv",
@@ -41,7 +41,7 @@
"sonarr": {
"wanted": "Gesucht",
"queued": "In Warteschlange",
- "series": "Serie"
+ "series": "Serien"
},
"radarr": {
"wanted": "Gesucht",
@@ -176,30 +176,30 @@
"failedLoginsLast24H": "fehlerhafte Anmeldungen (24h)"
},
"proxmox": {
- "mem": "MEM",
+ "mem": "RAM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
},
"unifi": {
- "users": "Users",
- "uptime": "System Uptime",
- "days": "Days",
+ "users": "Benutzer",
+ "uptime": "System-Betriebszeit",
+ "days": "Tage",
"wan": "WAN",
- "lan_users": "LAN Users",
- "wlan_users": "WLAN Users",
- "up": "UP",
- "down": "DOWN",
- "wait": "Please wait"
+ "lan_users": "LAN Benutzer",
+ "wlan_users": "WLAN Benutzer",
+ "up": "SENDEN",
+ "down": "EMPFANGEN",
+ "wait": "Bitte warten"
},
"plex": {
- "streams": "Active Streams",
- "movies": "Movies",
- "tv": "TV Shows"
+ "streams": "Aktive Streams",
+ "movies": "Filme",
+ "tv": "TV Sendungen"
},
"glances": {
"cpu": "CPU",
- "mem": "MEM",
- "wait": "Please wait"
+ "mem": "RAM",
+ "wait": "Bitte warten"
}
}
diff --git a/public/locales/es/common.json b/public/locales/es/common.json
index 7335f88b..25e153a1 100644
--- a/public/locales/es/common.json
+++ b/public/locales/es/common.json
@@ -178,28 +178,28 @@
"proxmox": {
"mem": "Memoria",
"cpu": "Procesador",
- "lxc": "LXC",
- "vms": "VMs"
+ "lxc": "Contenedores Linux",
+ "vms": "Máquinas Virtuales"
},
"unifi": {
- "up": "UP",
- "users": "Users",
- "uptime": "System Uptime",
- "days": "Days",
- "wan": "WAN",
- "lan_users": "LAN Users",
- "wlan_users": "WLAN Users",
- "down": "DOWN",
- "wait": "Please wait"
+ "up": "LEVANTADO",
+ "users": "Usuarios",
+ "uptime": "Tiempo de actividad",
+ "days": "Días",
+ "wan": "Red WAN",
+ "lan_users": "Usuarios LAN",
+ "wlan_users": "Usuarios WLAN",
+ "down": "CAÍDO",
+ "wait": "Espere por favor"
},
"plex": {
- "streams": "Active Streams",
- "movies": "Movies",
- "tv": "TV Shows"
+ "streams": "Transmisiones Activas",
+ "movies": "Peliculas",
+ "tv": "Programas TV"
},
"glances": {
- "cpu": "CPU",
- "mem": "MEM",
- "wait": "Please wait"
+ "cpu": "Procesador",
+ "mem": "Memoria",
+ "wait": "Espere por favor"
}
}
diff --git a/public/locales/fr/common.json b/public/locales/fr/common.json
index 2f49a82e..b9e57dd3 100644
--- a/public/locales/fr/common.json
+++ b/public/locales/fr/common.json
@@ -198,8 +198,8 @@
"tv": "Séries TV"
},
"glances": {
- "cpu": "CPU",
- "mem": "MEM",
- "wait": "Please wait"
+ "cpu": "Cpu",
+ "mem": "Mém",
+ "wait": "Merci de patienter"
}
}
diff --git a/public/locales/zh-CN/common.json b/public/locales/zh-CN/common.json
index 6592fb1e..f0229a63 100644
--- a/public/locales/zh-CN/common.json
+++ b/public/locales/zh-CN/common.json
@@ -161,7 +161,7 @@
},
"mastodon": {
"user_count": "用户",
- "status_count": "Posts",
+ "status_count": "帖子",
"domain_count": "域"
},
"strelaysrv": {
@@ -182,24 +182,24 @@
"vms": "VMs"
},
"unifi": {
- "users": "Users",
- "uptime": "System Uptime",
- "days": "Days",
- "wan": "WAN",
- "lan_users": "LAN Users",
- "wlan_users": "WLAN Users",
- "up": "UP",
- "down": "DOWN",
+ "users": "用户",
+ "uptime": "系统运行时间",
+ "days": "天",
+ "wan": "广域网",
+ "lan_users": "局域网用户",
+ "wlan_users": "无线局域网用户",
+ "up": "向上",
+ "down": "向下",
"wait": "请稍候"
},
"plex": {
- "streams": "Active Streams",
- "movies": "Movies",
- "tv": "TV Shows"
+ "streams": "活动流",
+ "movies": "电影",
+ "tv": "电视节目"
},
"glances": {
- "cpu": "CPU",
- "mem": "MEM",
- "wait": "Please wait"
+ "cpu": "处理器",
+ "mem": "内存",
+ "wait": "请稍等"
}
}
diff --git a/src/components/version.jsx b/src/components/version.jsx
index 52211219..bc142e92 100644
--- a/src/components/version.jsx
+++ b/src/components/version.jsx
@@ -31,7 +31,23 @@ export default function Version() {
return (
- {version} ({revision.substring(0, 7)}, {formatDate(buildTime)})
+ {version === "main" || version === "dev" || version === "nightly" ? (
+ <>
+ {version} ({revision.substring(0, 7)}, {formatDate(buildTime)})
+ >
+ ) : (
+ releaseData &&
+ compareVersions(latestRelease.tag_name, version) > 0 && (
+
+ {version} ({revision.substring(0, 7)}, {formatDate(buildTime)})
+
+ )
+ )}
{version === "main" || version === "dev" || version === "nightly"
? null
diff --git a/src/components/widgets/unifi_console/unifi_console.jsx b/src/components/widgets/unifi_console/unifi_console.jsx
index 7427bd23..889a517f 100644
--- a/src/components/widgets/unifi_console/unifi_console.jsx
+++ b/src/components/widgets/unifi_console/unifi_console.jsx
@@ -10,7 +10,7 @@ export default function Widget({ options }) {
// eslint-disable-next-line no-param-reassign
options.type = "unifi_console";
- const { data: statsData, error: statsError } = useWidgetAPI(options, "stat/sites");
+ const { data: statsData, error: statsError } = useWidgetAPI(options, "stat/sites", { index: options.index });
if (statsError || statsData?.error) {
return (
diff --git a/src/pages/api/widgets/glances.js b/src/pages/api/widgets/glances.js
index 86992dd1..5d4622b8 100644
--- a/src/pages/api/widgets/glances.js
+++ b/src/pages/api/widgets/glances.js
@@ -1,29 +1,17 @@
import { httpProxy } from "utils/proxy/http";
import createLogger from "utils/logger";
-import { getSettings } from "utils/config/config";
+import { getPrivateWidgetOptions } from "utils/config/widget-helpers";
const logger = createLogger("glances");
export default async function handler(req, res) {
- const { id } = req.query;
+ const { index } = req.query;
- let errorMessage;
-
- let instanceID = "glances";
- if (id) { // multiple instances
- instanceID = id;
- }
- const settings = getSettings();
- const instanceSettings = settings[instanceID];
- if (!instanceSettings) {
- errorMessage = id ? `There is no glances section with id '${id}' in settings.yaml` : "There is no glances section in settings.yaml";
- logger.error(errorMessage);
- return res.status(400).json({ error: errorMessage });
- }
+ const privateWidgetOptions = await getPrivateWidgetOptions("glances", index);
- const url = instanceSettings?.url;
+ const url = privateWidgetOptions?.url;
if (!url) {
- errorMessage = "Missing Glances URL";
+ const errorMessage = "Missing Glances URL";
logger.error(errorMessage);
return res.status(400).json({ error: errorMessage });
}
@@ -32,8 +20,8 @@ export default async function handler(req, res) {
const headers = {
"Accept-Encoding": "application/json"
};
- if (instanceSettings.username && instanceSettings.password) {
- headers.Authorization = `Basic ${Buffer.from(`${instanceSettings.username}:${instanceSettings.password}`).toString("base64")}`
+ if (privateWidgetOptions.username && privateWidgetOptions.password) {
+ headers.Authorization = `Basic ${Buffer.from(`${privateWidgetOptions.username}:${privateWidgetOptions.password}`).toString("base64")}`
}
const params = { method: "GET", headers };
diff --git a/src/utils/config/api-response.js b/src/utils/config/api-response.js
index 809927d1..5cc1127e 100644
--- a/src/utils/config/api-response.js
+++ b/src/utils/config/api-response.js
@@ -6,6 +6,7 @@ import yaml from "js-yaml";
import checkAndCopyConfig from "utils/config/config";
import { servicesFromConfig, servicesFromDocker, cleanServiceGroups } from "utils/config/service-helpers";
+import { cleanWidgetGroups, widgetsFromConfig } from "utils/config/widget-helpers";
export async function bookmarksResponse() {
checkAndCopyConfig("bookmarks.yaml");
@@ -29,21 +30,17 @@ export async function bookmarksResponse() {
}
export async function widgetsResponse() {
- checkAndCopyConfig("widgets.yaml");
+ let configuredWidgets;
- const widgetsYaml = path.join(process.cwd(), "config", "widgets.yaml");
- const fileContents = await fs.readFile(widgetsYaml, "utf8");
- const widgets = yaml.load(fileContents);
+ try {
+ configuredWidgets = cleanWidgetGroups(await widgetsFromConfig());
+ } catch (e) {
+ console.error("Failed to load widgets, please check widgets.yaml for errors or remove example entries.");
+ if (e) console.error(e);
+ configuredWidgets = [];
+ }
- if (!widgets) return [];
-
- // map easy to write YAML objects into easy to consume JS arrays
- const widgetsArray = widgets.map((group) => ({
- type: Object.keys(group)[0],
- options: { ...group[Object.keys(group)[0]] },
- }));
-
- return widgetsArray;
+ return configuredWidgets;
}
export async function servicesResponse() {
diff --git a/src/utils/config/config.js b/src/utils/config/config.js
index 16861de8..0003c589 100644
--- a/src/utils/config/config.js
+++ b/src/utils/config/config.js
@@ -33,4 +33,4 @@ export function getSettings() {
const settingsYaml = join(process.cwd(), "config", "settings.yaml");
const fileContents = readFileSync(settingsYaml, "utf8");
return yaml.load(fileContents);
-}
+}
\ No newline at end of file
diff --git a/src/utils/config/service-helpers.js b/src/utils/config/service-helpers.js
index 99ce34d1..15740d22 100644
--- a/src/utils/config/service-helpers.js
+++ b/src/utils/config/service-helpers.js
@@ -165,4 +165,4 @@ export default async function getServiceWidget(group, service) {
}
return false;
-}
+}
\ No newline at end of file
diff --git a/src/utils/config/widget-helpers.js b/src/utils/config/widget-helpers.js
new file mode 100644
index 00000000..c9bdd0fb
--- /dev/null
+++ b/src/utils/config/widget-helpers.js
@@ -0,0 +1,73 @@
+import { promises as fs } from "fs";
+import path from "path";
+
+import yaml from "js-yaml";
+
+import checkAndCopyConfig from "utils/config/config";
+
+export async function widgetsFromConfig() {
+ checkAndCopyConfig("widgets.yaml");
+
+ const widgetsYaml = path.join(process.cwd(), "config", "widgets.yaml");
+ const fileContents = await fs.readFile(widgetsYaml, "utf8");
+ const widgets = yaml.load(fileContents);
+
+ if (!widgets) return [];
+
+ // map easy to write YAML objects into easy to consume JS arrays
+ const widgetsArray = widgets.map((group, index) => ({
+ type: Object.keys(group)[0],
+ options: {
+ index,
+ ...group[Object.keys(group)[0]]
+ },
+ }));
+ return widgetsArray;
+}
+
+export async function cleanWidgetGroups(widgets) {
+ return widgets.map((widget, index) => {
+ const sanitizedOptions = widget.options;
+ const optionKeys = Object.keys(sanitizedOptions);
+ ["url", "username", "password", "key"].forEach((pO) => {
+ if (optionKeys.includes(pO)) {
+ delete sanitizedOptions[pO];
+ }
+ });
+
+ return {
+ type: widget.type,
+ options: {
+ index,
+ ...sanitizedOptions
+ },
+ }
+ });
+}
+
+export async function getPrivateWidgetOptions(type, widgetIndex) {
+ const widgets = await widgetsFromConfig();
+
+ const privateOptions = widgets.map((widget) => {
+ const {
+ index,
+ url,
+ username,
+ password,
+ key
+ } = widget.options;
+
+ return {
+ type: widget.type,
+ options: {
+ index,
+ url,
+ username,
+ password,
+ key
+ },
+ }
+ });
+
+ return (type !== undefined && widgetIndex !== undefined) ? privateOptions.find(o => o.type === type && o.options.index === parseInt(widgetIndex, 10))?.options : privateOptions;
+}
\ No newline at end of file
diff --git a/src/widgets/unifi/proxy.js b/src/widgets/unifi/proxy.js
index 53ee49f0..dc1c437b 100644
--- a/src/widgets/unifi/proxy.js
+++ b/src/widgets/unifi/proxy.js
@@ -3,8 +3,8 @@ import cache from "memory-cache";
import { formatApiCall } from "utils/proxy/api-helpers";
import { httpProxy } from "utils/proxy/http";
import { addCookieToJar, setCookieHeader } from "utils/proxy/cookie-jar";
-import { getSettings } from "utils/config/config";
import getServiceWidget from "utils/config/service-helpers";
+import { getPrivateWidgetOptions } from "utils/config/widget-helpers";
import createLogger from "utils/logger";
import widgets from "widgets/widgets";
@@ -15,13 +15,13 @@ const logger = createLogger(proxyName);
async function getWidget(req) {
const { group, service, type } = req.query;
-
+
let widget = null;
- if (type === "unifi_console") {
- const settings = getSettings();
- widget = settings.unifi_console;
+ if (type === "unifi_console") { // info widget
+ const index = req.query?.query ? JSON.parse(req.query.query).index : undefined;
+ widget = await getPrivateWidgetOptions(type, index);
if (!widget) {
- logger.debug("There is no unifi_console section in settings.yaml");
+ logger.debug("Error retrieving settings for this Unifi widget");
return null;
}
widget.type = "unifi";