Merge branch 'benphelps:main' into main

This commit is contained in:
Karl0ss 2023-06-13 17:42:48 +01:00 committed by GitHub
commit 32c4eb87e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 139 additions and 133 deletions

View File

@ -194,7 +194,7 @@
"lidarr": { "lidarr": {
"wanted": "مطلوب", "wanted": "مطلوب",
"queued": "في الإنتظار", "queued": "في الإنتظار",
"albums": "ألبومات" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "مطلوب", "wanted": "مطلوب",

View File

@ -132,7 +132,7 @@
"lidarr": { "lidarr": {
"wanted": "Wanted", "wanted": "Wanted",
"queued": "Queued", "queued": "Queued",
"albums": "Albums" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Wanted", "wanted": "Wanted",

View File

@ -177,7 +177,7 @@
"lidarr": { "lidarr": {
"wanted": "Volgut", "wanted": "Volgut",
"queued": "En cua", "queued": "En cua",
"albums": "Àlbums" "artists": "Artists"
}, },
"adguard": { "adguard": {
"queries": "Consultes", "queries": "Consultes",

View File

@ -148,7 +148,7 @@
"lidarr": { "lidarr": {
"wanted": "Hledané", "wanted": "Hledané",
"queued": "Ve frontě", "queued": "Ve frontě",
"albums": "Alba" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Hledané", "wanted": "Hledané",

View File

@ -16,7 +16,7 @@
"lidarr": { "lidarr": {
"wanted": "Ønsket", "wanted": "Ønsket",
"queued": "I Kø", "queued": "I Kø",
"albums": "Albums" "artists": "Artists"
}, },
"jellyseerr": { "jellyseerr": {
"available": "Tilgængelig", "available": "Tilgængelig",

View File

@ -177,7 +177,7 @@
"lidarr": { "lidarr": {
"wanted": "Gesucht", "wanted": "Gesucht",
"queued": "In Warteschlange", "queued": "In Warteschlange",
"albums": "Alben" "artists": "Artists"
}, },
"adguard": { "adguard": {
"queries": "Anfragen", "queries": "Anfragen",

View File

@ -227,7 +227,7 @@
"lidarr": { "lidarr": {
"wanted": "Θέλετε", "wanted": "Θέλετε",
"queued": "Στη σειρά", "queued": "Στη σειρά",
"albums": "Δίσκοι" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Θέλετε", "wanted": "Θέλετε",

View File

@ -209,7 +209,7 @@
"lidarr": { "lidarr": {
"wanted": "Wanted", "wanted": "Wanted",
"queued": "Queued", "queued": "Queued",
"albums": "Albums" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Wanted", "wanted": "Wanted",

View File

@ -146,7 +146,7 @@
"lidarr": { "lidarr": {
"wanted": "Wanted", "wanted": "Wanted",
"queued": "Queued", "queued": "Queued",
"albums": "Albumoj" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Wanted", "wanted": "Wanted",

View File

@ -67,16 +67,16 @@
"wanted": "Buscando", "wanted": "Buscando",
"queued": "En cola", "queued": "En cola",
"series": "Series", "series": "Series",
"queue": "Queue", "queue": "Poner a la cola",
"unknown": "Unknown" "unknown": "Desconocido"
}, },
"radarr": { "radarr": {
"wanted": "Buscando", "wanted": "Buscando",
"queued": "En cola", "queued": "En cola",
"movies": "Películas", "movies": "Películas",
"missing": "Faltan", "missing": "Faltan",
"queue": "Queue", "queue": "Poner a la cola",
"unknown": "Unknown" "unknown": "Desconocido"
}, },
"readarr": { "readarr": {
"wanted": "Buscando", "wanted": "Buscando",
@ -177,7 +177,7 @@
"lidarr": { "lidarr": {
"queued": "En cola", "queued": "En cola",
"wanted": "Buscando", "wanted": "Buscando",
"albums": "Álbumes" "artists": "Artistas"
}, },
"adguard": { "adguard": {
"queries": "Consultas", "queries": "Consultas",

View File

@ -109,7 +109,7 @@
"lidarr": { "lidarr": {
"wanted": "Haluttu", "wanted": "Haluttu",
"queued": "Jonossa", "queued": "Jonossa",
"albums": "Albumeja" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Haluttu", "wanted": "Haluttu",

View File

@ -67,16 +67,16 @@
"wanted": "Demande", "wanted": "Demande",
"queued": "Attente", "queued": "Attente",
"series": "Séries", "series": "Séries",
"queue": "Queue", "queue": "Attente",
"unknown": "Unknown" "unknown": "Inconnu"
}, },
"radarr": { "radarr": {
"wanted": "Demande", "wanted": "Demande",
"queued": "Attente", "queued": "Attente",
"movies": "Films", "movies": "Films",
"missing": "Manquant", "missing": "Manquant",
"queue": "Queue", "queue": "Attente",
"unknown": "Unknown" "unknown": "Inconnu"
}, },
"readarr": { "readarr": {
"wanted": "Demande", "wanted": "Demande",
@ -177,7 +177,7 @@
"lidarr": { "lidarr": {
"wanted": "Demandé", "wanted": "Demandé",
"queued": "En queue", "queued": "En queue",
"albums": "Albums" "artists": "Artistes"
}, },
"adguard": { "adguard": {
"queries": "Requêtes", "queries": "Requêtes",
@ -401,7 +401,7 @@
"queue": "À traiter", "queue": "À traiter",
"processed": "Traité", "processed": "Traité",
"errored": "En erreur", "errored": "En erreur",
"saved": "Gagné" "saved": "Libéré"
}, },
"miniflux": { "miniflux": {
"read": "Lu", "read": "Lu",

View File

@ -109,7 +109,7 @@
"lidarr": { "lidarr": {
"wanted": "מבוקש", "wanted": "מבוקש",
"queued": "בתור", "queued": "בתור",
"albums": "אלבומים" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "מבוקש", "wanted": "מבוקש",

View File

@ -170,7 +170,7 @@
"lidarr": { "lidarr": {
"wanted": "Wanted", "wanted": "Wanted",
"queued": "Queued", "queued": "Queued",
"albums": "Albums" "artists": "Artists"
}, },
"overseerr": { "overseerr": {
"pending": "Pending", "pending": "Pending",

View File

@ -140,7 +140,7 @@
"lidarr": { "lidarr": {
"wanted": "Zatraženo", "wanted": "Zatraženo",
"queued": "U redu čekanja", "queued": "U redu čekanja",
"albums": "Albumi" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Zatraženo", "wanted": "Zatraženo",

View File

@ -31,9 +31,9 @@
"healthy": "Healthy" "healthy": "Healthy"
}, },
"lidarr": { "lidarr": {
"albums": "Albumok",
"wanted": "Keresett", "wanted": "Keresett",
"queued": "Sorban áll" "queued": "Sorban áll",
"artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Keresett", "wanted": "Keresett",

View File

@ -70,7 +70,7 @@
"lidarr": { "lidarr": {
"wanted": "Wanted", "wanted": "Wanted",
"queued": "Queued", "queued": "Queued",
"albums": "Albums" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Wanted", "wanted": "Wanted",

View File

@ -177,7 +177,7 @@
"lidarr": { "lidarr": {
"wanted": "Mancanti", "wanted": "Mancanti",
"queued": "In coda", "queued": "In coda",
"albums": "Album" "artists": "Artists"
}, },
"adguard": { "adguard": {
"queries": "Interrogazioni", "queries": "Interrogazioni",

View File

@ -63,7 +63,7 @@
"resources": { "resources": {
"cpu": "CPU", "cpu": "CPU",
"total": "合計", "total": "合計",
"free": "フリー", "free": "Free",
"used": "使用", "used": "使用",
"load": "ロード", "load": "ロード",
"mem": "MEM", "mem": "MEM",
@ -208,7 +208,7 @@
"lidarr": { "lidarr": {
"wanted": "募集中", "wanted": "募集中",
"queued": "キュー", "queued": "キュー",
"albums": "アルバム" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "募集中", "wanted": "募集中",
@ -609,11 +609,11 @@
"ago": "{{value}} 前" "ago": "{{value}} 前"
}, },
"qnap": { "qnap": {
"cpuUsage": "CPU Usage", "cpuUsage": "CPU使用量",
"memUsage": "MEM Usage", "memUsage": "MEM使用量",
"systemTempC": "System Temp", "systemTempC": "システム温度",
"poolUsage": "Pool Usage", "poolUsage": "プール使用量",
"volumeUsage": "Volume Usage", "volumeUsage": "ボリューム使用量",
"invalid": "Invalid" "invalid": "Invalid"
}, },
"pfsense": { "pfsense": {
@ -633,11 +633,11 @@
}, },
"evcc": { "evcc": {
"watt_hour": "Wh", "watt_hour": "Wh",
"pv_power": "Production", "pv_power": "発電量",
"battery_soc": "Battery", "battery_soc": "バッテリー",
"grid_power": "Grid", "grid_power": "グリッド",
"home_power": "Consumption", "home_power": "消費",
"charge_power": "Charger" "charge_power": "チャージャー"
}, },
"pialert": { "pialert": {
"total": "Total", "total": "Total",

View File

@ -178,7 +178,7 @@
"lidarr": { "lidarr": {
"wanted": "요청", "wanted": "요청",
"queued": "대기 중", "queued": "대기 중",
"albums": "앨범" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "요청", "wanted": "요청",

View File

@ -169,7 +169,7 @@
"lidarr": { "lidarr": {
"wanted": "Wanted", "wanted": "Wanted",
"queued": "Queued", "queued": "Queued",
"albums": "Albumi" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Wanted", "wanted": "Wanted",

View File

@ -33,8 +33,8 @@
}, },
"lidarr": { "lidarr": {
"queued": "Dibaris Gilir", "queued": "Dibaris Gilir",
"albums": "Album", "wanted": "Mahu",
"wanted": "Mahu" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Mahu", "wanted": "Mahu",

View File

@ -177,7 +177,7 @@
"lidarr": { "lidarr": {
"wanted": "Wanted", "wanted": "Wanted",
"queued": "Queued", "queued": "Queued",
"albums": "Albums" "artists": "Artists"
}, },
"adguard": { "adguard": {
"queries": "Queries", "queries": "Queries",

View File

@ -177,7 +177,7 @@
"lidarr": { "lidarr": {
"wanted": "Gezocht", "wanted": "Gezocht",
"queued": "In de wachtrij", "queued": "In de wachtrij",
"albums": "Albums" "artists": "Artists"
}, },
"adguard": { "adguard": {
"queries": "Queries", "queries": "Queries",

View File

@ -125,7 +125,7 @@
"lidarr": { "lidarr": {
"wanted": "Poszukiwane", "wanted": "Poszukiwane",
"queued": "W kolejce", "queued": "W kolejce",
"albums": "Albumy" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Poszukiwane", "wanted": "Poszukiwane",

View File

@ -127,7 +127,7 @@
"lidarr": { "lidarr": {
"wanted": "Desejado", "wanted": "Desejado",
"queued": "Na fila", "queued": "Na fila",
"albums": "Álbuns" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Desejado", "wanted": "Desejado",

View File

@ -190,7 +190,7 @@
"lidarr": { "lidarr": {
"queued": "Enfileirado", "queued": "Enfileirado",
"wanted": "Desejado", "wanted": "Desejado",
"albums": "Álbuns" "artists": "Artists"
}, },
"adguard": { "adguard": {
"queries": "Consultas", "queries": "Consultas",
@ -590,12 +590,12 @@
"switches_on": "Interruptores Ligados" "switches_on": "Interruptores Ligados"
}, },
"freshrss": { "freshrss": {
"subscriptions": "Subscriptions", "subscriptions": "Assinaturas",
"unread": "Unread" "unread": "Não lida"
}, },
"channelsdvrserver": { "channelsdvrserver": {
"shows": "Shows", "shows": "Shows",
"recordings": "Recordings", "recordings": "Gravações",
"scheduled": "Scheduled", "scheduled": "Scheduled",
"passes": "Passes" "passes": "Passes"
}, },
@ -637,16 +637,16 @@
}, },
"caddy": { "caddy": {
"upstreams": "Upstreams", "upstreams": "Upstreams",
"requests": "Current requests", "requests": "Solicitações atuais",
"requests_failed": "Failed requests" "requests_failed": "Solicitações com falha"
}, },
"evcc": { "evcc": {
"pv_power": "Production", "pv_power": "Produção",
"battery_soc": "Battery", "battery_soc": "Bateria",
"grid_power": "Grid", "grid_power": "Grade",
"home_power": "Consumption", "home_power": "Consumo",
"charge_power": "Charger", "charge_power": "Carregador",
"watt_hour": "Wh" "watt_hour": "Kw"
}, },
"pialert": { "pialert": {
"total": "Total", "total": "Total",

View File

@ -149,7 +149,7 @@
"lidarr": { "lidarr": {
"wanted": "Dorite", "wanted": "Dorite",
"queued": "În coadă", "queued": "În coadă",
"albums": "Albume" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Dorite", "wanted": "Dorite",

View File

@ -177,7 +177,7 @@
"lidarr": { "lidarr": {
"wanted": "Хотел", "wanted": "Хотел",
"queued": "В очереди", "queued": "В очереди",
"albums": "Альбомы" "artists": "Artists"
}, },
"adguard": { "adguard": {
"queries": "Запросы", "queries": "Запросы",

View File

@ -288,7 +288,7 @@
"lidarr": { "lidarr": {
"wanted": "Wanted", "wanted": "Wanted",
"queued": "Queued", "queued": "Queued",
"albums": "Albums" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Wanted", "wanted": "Wanted",

View File

@ -250,7 +250,7 @@
"lidarr": { "lidarr": {
"wanted": "Iskano", "wanted": "Iskano",
"queued": "V vrsti", "queued": "V vrsti",
"albums": "Albumi" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Iskano", "wanted": "Iskano",

View File

@ -132,7 +132,7 @@
"lidarr": { "lidarr": {
"wanted": "Wanted", "wanted": "Wanted",
"queued": "Queued", "queued": "Queued",
"albums": "Albums" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Wanted", "wanted": "Wanted",

View File

@ -103,7 +103,7 @@
"lidarr": { "lidarr": {
"wanted": "Eftersöker", "wanted": "Eftersöker",
"queued": "I kö", "queued": "I kö",
"albums": "Album" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Eftersökt", "wanted": "Eftersökt",

View File

@ -126,7 +126,7 @@
"lidarr": { "lidarr": {
"wanted": "కావలెను", "wanted": "కావలెను",
"queued": "క్యూయూఎడ్", "queued": "క్యూయూఎడ్",
"albums": "ఆల్బములు" "artists": "Artists"
}, },
"bazarr": { "bazarr": {
"missingEpisodes": "ఎపిసోడ్‌లు లేవు", "missingEpisodes": "ఎపిసోడ్‌లు లేవు",

View File

@ -225,7 +225,7 @@
"lidarr": { "lidarr": {
"wanted": "Wanted", "wanted": "Wanted",
"queued": "Queued", "queued": "Queued",
"albums": "Albums" "artists": "Artists"
}, },
"ombi": { "ombi": {
"pending": "Pending", "pending": "Pending",

View File

@ -132,7 +132,7 @@
"lidarr": { "lidarr": {
"wanted": "Aranan", "wanted": "Aranan",
"queued": "Kuyrukta", "queued": "Kuyrukta",
"albums": "Albümler" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "Aranan", "wanted": "Aranan",

View File

@ -233,21 +233,21 @@
"wanted": "Розшукується", "wanted": "Розшукується",
"queued": "У черзі", "queued": "У черзі",
"series": "Серії", "series": "Серії",
"queue": "Queue", "queue": "Черга",
"unknown": "Unknown" "unknown": "Невідомо"
}, },
"radarr": { "radarr": {
"wanted": "Розшукується", "wanted": "Розшукується",
"missing": "Відсутній", "missing": "Відсутній",
"queued": "У черзі", "queued": "У черзі",
"movies": "Фільми", "movies": "Фільми",
"queue": "Queue", "queue": "Черга",
"unknown": "Unknown" "unknown": "Невідомо"
}, },
"lidarr": { "lidarr": {
"wanted": "Розшукується", "wanted": "Розшукується",
"queued": "У черзі", "queued": "У черзі",
"albums": "Альбоми" "artists": "Виконавці"
}, },
"traefik": { "traefik": {
"middleware": "Проміжне програмне забезпечення", "middleware": "Проміжне програмне забезпечення",

View File

@ -177,7 +177,7 @@
"lidarr": { "lidarr": {
"wanted": "Wanted", "wanted": "Wanted",
"queued": "Queued", "queued": "Queued",
"albums": "Albums" "artists": "Artists"
}, },
"adguard": { "adguard": {
"queries": "Queries", "queries": "Queries",

View File

@ -109,7 +109,7 @@
"lidarr": { "lidarr": {
"wanted": "想睇", "wanted": "想睇",
"queued": "排緊隊", "queued": "排緊隊",
"albums": "專輯" "artists": "Artists"
}, },
"readarr": { "readarr": {
"wanted": "想睇", "wanted": "想睇",

View File

@ -177,7 +177,7 @@
"lidarr": { "lidarr": {
"wanted": "订阅", "wanted": "订阅",
"queued": "队列", "queued": "队列",
"albums": "相册" "artists": "Artists"
}, },
"adguard": { "adguard": {
"queries": "查询", "queries": "查询",

View File

@ -177,7 +177,7 @@
"lidarr": { "lidarr": {
"wanted": "關注中", "wanted": "關注中",
"queued": "已加入佇列", "queued": "已加入佇列",
"albums": "專輯" "artists": "Artists"
}, },
"adguard": { "adguard": {
"queries": "查詢", "queries": "查詢",

View File

@ -3,7 +3,7 @@ import classNames from "classnames";
import List from "components/services/list"; import List from "components/services/list";
import ResolvedIcon from "components/resolvedicon"; import ResolvedIcon from "components/resolvedicon";
export default function ServicesGroup({ services, layout, fiveColumns }) { export default function ServicesGroup({ group, services, layout, fiveColumns }) {
return ( return (
<div <div
key={services.name} key={services.name}
@ -21,7 +21,7 @@ export default function ServicesGroup({ services, layout, fiveColumns }) {
} }
<h2 className="text-theme-800 dark:text-theme-300 text-xl font-medium">{services.name}</h2> <h2 className="text-theme-800 dark:text-theme-300 text-xl font-medium">{services.name}</h2>
</div> </div>
<List services={services.services} layout={layout} /> <List group={group} services={services.services} layout={layout} />
</div> </div>
); );
} }

View File

@ -11,7 +11,7 @@ import Kubernetes from "widgets/kubernetes/component";
import { SettingsContext } from "utils/contexts/settings"; import { SettingsContext } from "utils/contexts/settings";
import ResolvedIcon from "components/resolvedicon"; import ResolvedIcon from "components/resolvedicon";
export default function Item({ service }) { export default function Item({ service, group }) {
const hasLink = service.href && service.href !== "#"; const hasLink = service.href && service.href !== "#";
const { settings } = useContext(SettingsContext); const { settings } = useContext(SettingsContext);
const showStats = (service.showStats === false) ? false : settings.showStats; const showStats = (service.showStats === false) ? false : settings.showStats;
@ -77,7 +77,7 @@ export default function Item({ service }) {
<div className="absolute top-0 right-0 w-1/2 flex flex-row justify-end gap-2 mr-2"> <div className="absolute top-0 right-0 w-1/2 flex flex-row justify-end gap-2 mr-2">
{service.ping && ( {service.ping && (
<div className="flex-shrink-0 flex items-center justify-center cursor-pointer"> <div className="flex-shrink-0 flex items-center justify-center cursor-pointer">
<Ping service={service} /> <Ping group={group} service={service.name} />
<span className="sr-only">Ping status</span> <span className="sr-only">Ping status</span>
</div> </div>
)} )}

View File

@ -14,7 +14,7 @@ const columnMap = [
"grid-cols-1 md:grid-cols-2 lg:grid-cols-8", "grid-cols-1 md:grid-cols-2 lg:grid-cols-8",
]; ];
export default function List({ services, layout }) { export default function List({ group, services, layout }) {
return ( return (
<ul <ul
className={classNames( className={classNames(
@ -23,7 +23,7 @@ export default function List({ services, layout }) {
)} )}
> >
{services.map((service) => ( {services.map((service) => (
<Item key={service.container ?? service.app ?? service.name} service={service} /> <Item key={service.container ?? service.app ?? service.name} service={service} group={group} />
))} ))}
</ul> </ul>
); );

View File

@ -1,9 +1,9 @@
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import useSWR from "swr"; import useSWR from "swr";
export default function Ping({ service }) { export default function Ping({ group, service }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { data, error } = useSWR(`/api/ping?${new URLSearchParams({ping: service.ping}).toString()}`, { const { data, error } = useSWR(`/api/ping?${new URLSearchParams({ group, service }).toString()}`, {
refreshInterval: 30000 refreshInterval: 30000
}); });
@ -23,7 +23,7 @@ export default function Ping({ service }) {
); );
} }
const statusText = `${service.ping}: HTTP status ${data.status}`; const statusText = `${service}: HTTP status ${data.status}`;
if (data.status > 403) { if (data.status > 403) {
return ( return (

View File

@ -9,10 +9,12 @@ function displayData(data) {
return (data.type === 'Buffer') ? Buffer.from(data).toString() : JSON.stringify(data, 4); return (data.type === 'Buffer') ? Buffer.from(data).toString() : JSON.stringify(data, 4);
} }
export default function Error({ error: err }) { export default function Error({ error }) {
const { t } = useTranslation(); const { t } = useTranslation();
const { error } = err?.data ?? { error: err }; if (error?.data?.error) {
error = error.data.error; // eslint-disable-line no-param-reassign
}
return ( return (
<details className="px-1 pb-1"> <details className="px-1 pb-1">

View File

@ -1,12 +1,22 @@
import { performance } from "perf_hooks"; import { performance } from "perf_hooks";
import { getServiceItem } from "utils/config/service-helpers";
import createLogger from "utils/logger"; import createLogger from "utils/logger";
import { httpProxy } from "utils/proxy/http"; import { httpProxy } from "utils/proxy/http";
const logger = createLogger("ping"); const logger = createLogger("ping");
export default async function handler(req, res) { export default async function handler(req, res) {
const { ping: pingURL } = req.query; const { group, service } = req.query;
const serviceItem = await getServiceItem(group, service);
if (!serviceItem) {
logger.debug(`No service item found for group ${group} named ${service}`);
return res.status(400).send({
error: "Unable to find service, see log for details.",
});
}
const { ping: pingURL } = serviceItem;
if (!pingURL) { if (!pingURL) {
logger.debug("No ping URL specified"); logger.debug("No ping URL specified");

View File

@ -252,7 +252,7 @@ function Home({ initialSettings }) {
/> />
<meta name="theme-color" content={themes[initialSettings.color || "slate"][initialSettings.theme || "dark"]} /> <meta name="theme-color" content={themes[initialSettings.color || "slate"][initialSettings.theme || "dark"]} />
</Head> </Head>
<div className="relative container m-auto flex flex-col justify-between z-10 h-full"> <div className="relative container m-auto flex flex-col justify-start z-10 h-full">
<div <div
className={classNames( className={classNames(
"flex flex-row flex-wrap justify-between", "flex flex-row flex-wrap justify-between",
@ -289,7 +289,7 @@ function Home({ initialSettings }) {
{services?.length > 0 && ( {services?.length > 0 && (
<div className="flex flex-wrap p-4 sm:p-8 sm:pt-4 items-start pb-2"> <div className="flex flex-wrap p-4 sm:p-8 sm:pt-4 items-start pb-2">
{services.map((group) => ( {services.map((group) => (
<ServicesGroup key={group.name} services={group} layout={initialSettings.layout?.[group.name]} fiveColumns={settings.fiveColumns} /> <ServicesGroup key={group.name} group={group.name} services={group} layout={initialSettings.layout?.[group.name]} fiveColumns={settings.fiveColumns} />
))} ))}
</div> </div>
)} )}
@ -302,14 +302,16 @@ function Home({ initialSettings }) {
</div> </div>
)} )}
<div className="flex p-8 pb-0 w-full justify-end"> <div className="flex flex-col mt-auto p-8 w-full">
{!initialSettings?.color && <ColorToggle />} <div className="flex w-full justify-end">
<Revalidate /> {!initialSettings?.color && <ColorToggle />}
{!initialSettings?.theme && <ThemeToggle />} <Revalidate />
</div> {!initialSettings?.theme && <ThemeToggle />}
</div>
<div className="flex p-8 pt-4 w-full justify-end"> <div className="flex mt-4 w-full justify-end">
{!initialSettings?.hideVersion && <Version />} {!initialSettings?.hideVersion && <Version />}
</div>
</div> </div>
</div> </div>
</> </>

View File

@ -328,16 +328,13 @@ export function cleanServiceGroups(groups) {
})); }));
} }
export default async function getServiceWidget(group, service) { export async function getServiceItem(group, service) {
const configuredServices = await servicesFromConfig(); const configuredServices = await servicesFromConfig();
const serviceGroup = configuredServices.find((g) => g.name === group); const serviceGroup = configuredServices.find((g) => g.name === group);
if (serviceGroup) { if (serviceGroup) {
const serviceEntry = serviceGroup.services.find((s) => s.name === service); const serviceEntry = serviceGroup.services.find((s) => s.name === service);
if (serviceEntry) { if (serviceEntry) return serviceEntry;
const { widget } = serviceEntry;
return widget;
}
} }
const discoveredServices = await servicesFromDocker(); const discoveredServices = await servicesFromDocker();
@ -345,20 +342,24 @@ export default async function getServiceWidget(group, service) {
const dockerServiceGroup = discoveredServices.find((g) => g.name === group); const dockerServiceGroup = discoveredServices.find((g) => g.name === group);
if (dockerServiceGroup) { if (dockerServiceGroup) {
const dockerServiceEntry = dockerServiceGroup.services.find((s) => s.name === service); const dockerServiceEntry = dockerServiceGroup.services.find((s) => s.name === service);
if (dockerServiceEntry) { if (dockerServiceEntry) return dockerServiceEntry;
const { widget } = dockerServiceEntry;
return widget;
}
} }
const kubernetesServices = await servicesFromKubernetes(); const kubernetesServices = await servicesFromKubernetes();
const kubernetesServiceGroup = kubernetesServices.find((g) => g.name === group); const kubernetesServiceGroup = kubernetesServices.find((g) => g.name === group);
if (kubernetesServiceGroup) { if (kubernetesServiceGroup) {
const kubernetesServiceEntry = kubernetesServiceGroup.services.find((s) => s.name === service); const kubernetesServiceEntry = kubernetesServiceGroup.services.find((s) => s.name === service);
if (kubernetesServiceEntry) { if (kubernetesServiceEntry) return kubernetesServiceEntry;
const { widget } = kubernetesServiceEntry; }
return widget;
} return false;
}
export default async function getServiceWidget(group, service) {
const serviceItem = await getServiceItem(group, service);
if (serviceItem) {
const { widget } = serviceItem;
return widget;
} }
return false; return false;

View File

@ -16,7 +16,7 @@ export default function Component({ service }) {
`/api/kubernetes/stats/${widget.namespace}/${widget.app}?${podSelectorString}`); `/api/kubernetes/stats/${widget.namespace}/${widget.app}?${podSelectorString}`);
if (statsError || statusError) { if (statsError || statusError) {
return <Container service={service} error={t("widget.api_error")} />; return <Container service={service} error={statsError ?? statusError} />;
} }
if (statusData && statusData.status !== "running") { if (statusData && statusData.status !== "running") {

View File

@ -9,23 +9,21 @@ export default function Component({ service }) {
const { widget } = service; const { widget } = service;
// album API endpoint can get massive, so we prevent calling if not included in fields see https://github.com/benphelps/homepage/discussions/1577 const { data: artistsData, error: artistsError } = useWidgetAPI(widget, "artist");
const showAlbums = widget.fields?.includes('albums') || !widget.fields;
const { data: albumsData, error: albumsError } = useWidgetAPI(widget, showAlbums ? "album" : "");
const { data: wantedData, error: wantedError } = useWidgetAPI(widget, "wanted/missing"); const { data: wantedData, error: wantedError } = useWidgetAPI(widget, "wanted/missing");
const { data: queueData, error: queueError } = useWidgetAPI(widget, "queue/status"); const { data: queueData, error: queueError } = useWidgetAPI(widget, "queue/status");
if (albumsError || wantedError || queueError) { if (artistsError || wantedError || queueError) {
const finalError = albumsError ?? wantedError ?? queueError; const finalError = artistsError ?? wantedError ?? queueError;
return <Container service={service} error={finalError} />; return <Container service={service} error={finalError} />;
} }
if ((showAlbums && !albumsData) || !wantedData || !queueData) { if (!artistsData || !wantedData || !queueData) {
return ( return (
<Container service={service}> <Container service={service}>
<Block label="lidarr.wanted" /> <Block label="lidarr.wanted" />
<Block label="lidarr.queued" /> <Block label="lidarr.queued" />
<Block label="lidarr.albums" /> <Block label="lidarr.artists" />
</Container> </Container>
); );
} }
@ -34,7 +32,7 @@ export default function Component({ service }) {
<Container service={service}> <Container service={service}>
<Block label="lidarr.wanted" value={t("common.number", { value: wantedData.totalRecords })} /> <Block label="lidarr.wanted" value={t("common.number", { value: wantedData.totalRecords })} />
<Block label="lidarr.queued" value={t("common.number", { value: queueData.totalCount })} /> <Block label="lidarr.queued" value={t("common.number", { value: queueData.totalCount })} />
{showAlbums && <Block label="lidarr.albums" value={t("common.number", { value: albumsData?.have })} />} <Block label="lidarr.artists" value={t("common.number", { value: artistsData.length })} />
</Container> </Container>
); );
} }

View File

@ -1,16 +1,12 @@
import genericProxyHandler from "utils/proxy/handlers/generic"; import genericProxyHandler from "utils/proxy/handlers/generic";
import { jsonArrayFilter } from "utils/proxy/api-helpers";
const widget = { const widget = {
api: "{url}/api/v1/{endpoint}?apikey={key}", api: "{url}/api/v1/{endpoint}?apikey={key}",
proxyHandler: genericProxyHandler, proxyHandler: genericProxyHandler,
mappings: { mappings: {
album: { artist: {
endpoint: "album", endpoint: "artist",
map: (data) => ({
have: jsonArrayFilter(data, (item) => item?.statistics?.percentOfTracks === 100).length,
}),
}, },
"wanted/missing": { "wanted/missing": {
endpoint: "wanted/missing", endpoint: "wanted/missing",

View File

@ -1,12 +1,8 @@
import { useTranslation } from "next-i18next";
import Container from "components/services/widget/container"; import Container from "components/services/widget/container";
import Block from "components/services/widget/block"; import Block from "components/services/widget/block";
import useWidgetAPI from "utils/proxy/use-widget-api"; import useWidgetAPI from "utils/proxy/use-widget-api";
export default function Component({ service }) { export default function Component({ service }) {
const { t } = useTranslation();
const { widget } = service; const { widget } = service;
const { data: containersData, error: containersError } = useWidgetAPI(widget, "docker/containers/json", { const { data: containersData, error: containersError } = useWidgetAPI(widget, "docker/containers/json", {
@ -27,8 +23,9 @@ export default function Component({ service }) {
); );
} }
if (containersData.error) { if (containersData.error || containersData.message) {
return <Container service={service} error={t("widget.api_error")} />; // containersData can be itself an error object e.g. if environment fails
return <Container service={service} error={ containersData?.error ?? containersData } />;
} }
const running = containersData.filter((c) => c.State === "running").length; const running = containersData.filter((c) => c.State === "running").length;