mirror of
https://github.com/karl0ss/homepage.git
synced 2025-05-02 05:23:39 +01:00
Merge branch 'master' into fix/icon
This commit is contained in:
commit
78a75a1ff9
@ -38,7 +38,7 @@
|
|||||||
- Images built for AMD64 (x86_64), ARM64, ARMv7 and ARMv6
|
- Images built for AMD64 (x86_64), ARM64, ARMv7 and ARMv6
|
||||||
- Supports all Raspberry Pi's, most SBCs & Apple Silicon
|
- Supports all Raspberry Pi's, most SBCs & Apple Silicon
|
||||||
- Full i18n support with automatic language detection
|
- 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/)
|
- Want to help translate? [Join the Weblate project](https://hosted.weblate.org/engage/homepage/)
|
||||||
- Service & Web Bookmarks
|
- Service & Web Bookmarks
|
||||||
- Docker Integration
|
- Docker Integration
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
"total": "Gesamt",
|
"total": "Gesamt",
|
||||||
"free": "Frei",
|
"free": "Frei",
|
||||||
"used": "Gebraucht",
|
"used": "Gebraucht",
|
||||||
"load": "Belastung",
|
"load": "Last",
|
||||||
"cpu": "CPU"
|
"cpu": "CPU"
|
||||||
},
|
},
|
||||||
"docker": {
|
"docker": {
|
||||||
@ -25,13 +25,13 @@
|
|||||||
"playing": "Spielen",
|
"playing": "Spielen",
|
||||||
"transcoding": "Transcodierung",
|
"transcoding": "Transcodierung",
|
||||||
"bitrate": "Bitrate",
|
"bitrate": "Bitrate",
|
||||||
"no_active": "Keine aktive Streams"
|
"no_active": "Keine aktiven Streams"
|
||||||
},
|
},
|
||||||
"tautulli": {
|
"tautulli": {
|
||||||
"playing": "Spielen",
|
"playing": "Spielen",
|
||||||
"transcoding": "Transcodierung",
|
"transcoding": "Transcodierung",
|
||||||
"bitrate": "Bitrate",
|
"bitrate": "Bitrate",
|
||||||
"no_active": "Keine aktiven streamen"
|
"no_active": "Keine aktiven Streams"
|
||||||
},
|
},
|
||||||
"rutorrent": {
|
"rutorrent": {
|
||||||
"active": "Aktiv",
|
"active": "Aktiv",
|
||||||
@ -41,7 +41,7 @@
|
|||||||
"sonarr": {
|
"sonarr": {
|
||||||
"wanted": "Gesucht",
|
"wanted": "Gesucht",
|
||||||
"queued": "In Warteschlange",
|
"queued": "In Warteschlange",
|
||||||
"series": "Serie"
|
"series": "Serien"
|
||||||
},
|
},
|
||||||
"radarr": {
|
"radarr": {
|
||||||
"wanted": "Gesucht",
|
"wanted": "Gesucht",
|
||||||
@ -176,30 +176,30 @@
|
|||||||
"failedLoginsLast24H": "fehlerhafte Anmeldungen (24h)"
|
"failedLoginsLast24H": "fehlerhafte Anmeldungen (24h)"
|
||||||
},
|
},
|
||||||
"proxmox": {
|
"proxmox": {
|
||||||
"mem": "MEM",
|
"mem": "RAM",
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"lxc": "LXC",
|
"lxc": "LXC",
|
||||||
"vms": "VMs"
|
"vms": "VMs"
|
||||||
},
|
},
|
||||||
"unifi": {
|
"unifi": {
|
||||||
"users": "Users",
|
"users": "Benutzer",
|
||||||
"uptime": "System Uptime",
|
"uptime": "System-Betriebszeit",
|
||||||
"days": "Days",
|
"days": "Tage",
|
||||||
"wan": "WAN",
|
"wan": "WAN",
|
||||||
"lan_users": "LAN Users",
|
"lan_users": "LAN Benutzer",
|
||||||
"wlan_users": "WLAN Users",
|
"wlan_users": "WLAN Benutzer",
|
||||||
"up": "UP",
|
"up": "SENDEN",
|
||||||
"down": "DOWN",
|
"down": "EMPFANGEN",
|
||||||
"wait": "Please wait"
|
"wait": "Bitte warten"
|
||||||
},
|
},
|
||||||
"plex": {
|
"plex": {
|
||||||
"streams": "Active Streams",
|
"streams": "Aktive Streams",
|
||||||
"movies": "Movies",
|
"movies": "Filme",
|
||||||
"tv": "TV Shows"
|
"tv": "TV Sendungen"
|
||||||
},
|
},
|
||||||
"glances": {
|
"glances": {
|
||||||
"cpu": "CPU",
|
"cpu": "CPU",
|
||||||
"mem": "MEM",
|
"mem": "RAM",
|
||||||
"wait": "Please wait"
|
"wait": "Bitte warten"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,28 +178,28 @@
|
|||||||
"proxmox": {
|
"proxmox": {
|
||||||
"mem": "Memoria",
|
"mem": "Memoria",
|
||||||
"cpu": "Procesador",
|
"cpu": "Procesador",
|
||||||
"lxc": "LXC",
|
"lxc": "Contenedores Linux",
|
||||||
"vms": "VMs"
|
"vms": "Máquinas Virtuales"
|
||||||
},
|
},
|
||||||
"unifi": {
|
"unifi": {
|
||||||
"up": "UP",
|
"up": "LEVANTADO",
|
||||||
"users": "Users",
|
"users": "Usuarios",
|
||||||
"uptime": "System Uptime",
|
"uptime": "Tiempo de actividad",
|
||||||
"days": "Days",
|
"days": "Días",
|
||||||
"wan": "WAN",
|
"wan": "Red WAN",
|
||||||
"lan_users": "LAN Users",
|
"lan_users": "Usuarios LAN",
|
||||||
"wlan_users": "WLAN Users",
|
"wlan_users": "Usuarios WLAN",
|
||||||
"down": "DOWN",
|
"down": "CAÍDO",
|
||||||
"wait": "Please wait"
|
"wait": "Espere por favor"
|
||||||
},
|
},
|
||||||
"plex": {
|
"plex": {
|
||||||
"streams": "Active Streams",
|
"streams": "Transmisiones Activas",
|
||||||
"movies": "Movies",
|
"movies": "Peliculas",
|
||||||
"tv": "TV Shows"
|
"tv": "Programas TV"
|
||||||
},
|
},
|
||||||
"glances": {
|
"glances": {
|
||||||
"cpu": "CPU",
|
"cpu": "Procesador",
|
||||||
"mem": "MEM",
|
"mem": "Memoria",
|
||||||
"wait": "Please wait"
|
"wait": "Espere por favor"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,8 +198,8 @@
|
|||||||
"tv": "Séries TV"
|
"tv": "Séries TV"
|
||||||
},
|
},
|
||||||
"glances": {
|
"glances": {
|
||||||
"cpu": "CPU",
|
"cpu": "Cpu",
|
||||||
"mem": "MEM",
|
"mem": "Mém",
|
||||||
"wait": "Please wait"
|
"wait": "Merci de patienter"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@
|
|||||||
},
|
},
|
||||||
"mastodon": {
|
"mastodon": {
|
||||||
"user_count": "用户",
|
"user_count": "用户",
|
||||||
"status_count": "Posts",
|
"status_count": "帖子",
|
||||||
"domain_count": "域"
|
"domain_count": "域"
|
||||||
},
|
},
|
||||||
"strelaysrv": {
|
"strelaysrv": {
|
||||||
@ -182,24 +182,24 @@
|
|||||||
"vms": "VMs"
|
"vms": "VMs"
|
||||||
},
|
},
|
||||||
"unifi": {
|
"unifi": {
|
||||||
"users": "Users",
|
"users": "用户",
|
||||||
"uptime": "System Uptime",
|
"uptime": "系统运行时间",
|
||||||
"days": "Days",
|
"days": "天",
|
||||||
"wan": "WAN",
|
"wan": "广域网",
|
||||||
"lan_users": "LAN Users",
|
"lan_users": "局域网用户",
|
||||||
"wlan_users": "WLAN Users",
|
"wlan_users": "无线局域网用户",
|
||||||
"up": "UP",
|
"up": "向上",
|
||||||
"down": "DOWN",
|
"down": "向下",
|
||||||
"wait": "请稍候"
|
"wait": "请稍候"
|
||||||
},
|
},
|
||||||
"plex": {
|
"plex": {
|
||||||
"streams": "Active Streams",
|
"streams": "活动流",
|
||||||
"movies": "Movies",
|
"movies": "电影",
|
||||||
"tv": "TV Shows"
|
"tv": "电视节目"
|
||||||
},
|
},
|
||||||
"glances": {
|
"glances": {
|
||||||
"cpu": "CPU",
|
"cpu": "处理器",
|
||||||
"mem": "MEM",
|
"mem": "内存",
|
||||||
"wait": "Please wait"
|
"wait": "请稍等"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,23 @@ export default function Version() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
<span className="text-xs text-theme-500 dark:text-theme-400">
|
<span className="text-xs text-theme-500 dark:text-theme-400">
|
||||||
|
{version === "main" || version === "dev" || version === "nightly" ? (
|
||||||
|
<>
|
||||||
{version} ({revision.substring(0, 7)}, {formatDate(buildTime)})
|
{version} ({revision.substring(0, 7)}, {formatDate(buildTime)})
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
releaseData &&
|
||||||
|
compareVersions(latestRelease.tag_name, version) > 0 && (
|
||||||
|
<a
|
||||||
|
href={`https://github.com/benphelps/homepage/releases/tag/${version}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="ml-2 text-xs text-theme-500 dark:text-theme-400 flex flex-row items-center"
|
||||||
|
>
|
||||||
|
{version} ({revision.substring(0, 7)}, {formatDate(buildTime)})
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
{version === "main" || version === "dev" || version === "nightly"
|
{version === "main" || version === "dev" || version === "nightly"
|
||||||
? null
|
? null
|
||||||
|
@ -10,7 +10,7 @@ export default function Widget({ options }) {
|
|||||||
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
options.type = "unifi_console";
|
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) {
|
if (statsError || statsData?.error) {
|
||||||
return (
|
return (
|
||||||
|
@ -1,29 +1,17 @@
|
|||||||
import { httpProxy } from "utils/proxy/http";
|
import { httpProxy } from "utils/proxy/http";
|
||||||
import createLogger from "utils/logger";
|
import createLogger from "utils/logger";
|
||||||
import { getSettings } from "utils/config/config";
|
import { getPrivateWidgetOptions } from "utils/config/widget-helpers";
|
||||||
|
|
||||||
const logger = createLogger("glances");
|
const logger = createLogger("glances");
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
const { id } = req.query;
|
const { index } = req.query;
|
||||||
|
|
||||||
let errorMessage;
|
const privateWidgetOptions = await getPrivateWidgetOptions("glances", index);
|
||||||
|
|
||||||
let instanceID = "glances";
|
const url = privateWidgetOptions?.url;
|
||||||
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 url = instanceSettings?.url;
|
|
||||||
if (!url) {
|
if (!url) {
|
||||||
errorMessage = "Missing Glances URL";
|
const errorMessage = "Missing Glances URL";
|
||||||
logger.error(errorMessage);
|
logger.error(errorMessage);
|
||||||
return res.status(400).json({ error: errorMessage });
|
return res.status(400).json({ error: errorMessage });
|
||||||
}
|
}
|
||||||
@ -32,8 +20,8 @@ export default async function handler(req, res) {
|
|||||||
const headers = {
|
const headers = {
|
||||||
"Accept-Encoding": "application/json"
|
"Accept-Encoding": "application/json"
|
||||||
};
|
};
|
||||||
if (instanceSettings.username && instanceSettings.password) {
|
if (privateWidgetOptions.username && privateWidgetOptions.password) {
|
||||||
headers.Authorization = `Basic ${Buffer.from(`${instanceSettings.username}:${instanceSettings.password}`).toString("base64")}`
|
headers.Authorization = `Basic ${Buffer.from(`${privateWidgetOptions.username}:${privateWidgetOptions.password}`).toString("base64")}`
|
||||||
}
|
}
|
||||||
const params = { method: "GET", headers };
|
const params = { method: "GET", headers };
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import yaml from "js-yaml";
|
|||||||
|
|
||||||
import checkAndCopyConfig from "utils/config/config";
|
import checkAndCopyConfig from "utils/config/config";
|
||||||
import { servicesFromConfig, servicesFromDocker, cleanServiceGroups } from "utils/config/service-helpers";
|
import { servicesFromConfig, servicesFromDocker, cleanServiceGroups } from "utils/config/service-helpers";
|
||||||
|
import { cleanWidgetGroups, widgetsFromConfig } from "utils/config/widget-helpers";
|
||||||
|
|
||||||
export async function bookmarksResponse() {
|
export async function bookmarksResponse() {
|
||||||
checkAndCopyConfig("bookmarks.yaml");
|
checkAndCopyConfig("bookmarks.yaml");
|
||||||
@ -29,21 +30,17 @@ export async function bookmarksResponse() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function widgetsResponse() {
|
export async function widgetsResponse() {
|
||||||
checkAndCopyConfig("widgets.yaml");
|
let configuredWidgets;
|
||||||
|
|
||||||
const widgetsYaml = path.join(process.cwd(), "config", "widgets.yaml");
|
try {
|
||||||
const fileContents = await fs.readFile(widgetsYaml, "utf8");
|
configuredWidgets = cleanWidgetGroups(await widgetsFromConfig());
|
||||||
const widgets = yaml.load(fileContents);
|
} 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 [];
|
return configuredWidgets;
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function servicesResponse() {
|
export async function servicesResponse() {
|
||||||
|
73
src/utils/config/widget-helpers.js
Normal file
73
src/utils/config/widget-helpers.js
Normal file
@ -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;
|
||||||
|
}
|
@ -3,8 +3,8 @@ import cache from "memory-cache";
|
|||||||
import { formatApiCall } from "utils/proxy/api-helpers";
|
import { formatApiCall } from "utils/proxy/api-helpers";
|
||||||
import { httpProxy } from "utils/proxy/http";
|
import { httpProxy } from "utils/proxy/http";
|
||||||
import { addCookieToJar, setCookieHeader } from "utils/proxy/cookie-jar";
|
import { addCookieToJar, setCookieHeader } from "utils/proxy/cookie-jar";
|
||||||
import { getSettings } from "utils/config/config";
|
|
||||||
import getServiceWidget from "utils/config/service-helpers";
|
import getServiceWidget from "utils/config/service-helpers";
|
||||||
|
import { getPrivateWidgetOptions } from "utils/config/widget-helpers";
|
||||||
import createLogger from "utils/logger";
|
import createLogger from "utils/logger";
|
||||||
import widgets from "widgets/widgets";
|
import widgets from "widgets/widgets";
|
||||||
|
|
||||||
@ -17,11 +17,11 @@ async function getWidget(req) {
|
|||||||
const { group, service, type } = req.query;
|
const { group, service, type } = req.query;
|
||||||
|
|
||||||
let widget = null;
|
let widget = null;
|
||||||
if (type === "unifi_console") {
|
if (type === "unifi_console") { // info widget
|
||||||
const settings = getSettings();
|
const index = req.query?.query ? JSON.parse(req.query.query).index : undefined;
|
||||||
widget = settings.unifi_console;
|
widget = await getPrivateWidgetOptions(type, index);
|
||||||
if (!widget) {
|
if (!widget) {
|
||||||
logger.debug("There is no unifi_console section in settings.yaml");
|
logger.debug("Error retrieving settings for this Unifi widget");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
widget.type = "unifi";
|
widget.type = "unifi";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user