pull/141/head
Jason Fischer 2 years ago
commit 95b6ea0e23

@ -16,7 +16,7 @@
- Container status (Running / Stopped) & statistics (CPU, Memory, Network)
- Automatic service discovery (via labels)
* Service Integration
- Sonarr, Radarr, Readarr, Emby, Jellyfin, Tautulli (Plex)
- Sonarr, Radarr, Readarr, Prowlarr, Emby, Jellyfin, Tautulli (Plex)
- Ombi, Overseerr, Jellyseerr, NZBGet, SABnzbd, ruTorrent
- Portainer, Traefik, Speedtest Tracker, PiHole, Nginx Proxy Manager, Gotify
* Information Providers
@ -78,6 +78,8 @@ pnpm install
pnpm build
```
If this is your first time starting, copy the `src/skeleton` directory to `config/` to populate initial example config files.
Finally, run the server:
```bash
@ -131,4 +133,4 @@ Huge thanks to the all the contributors who have helped make this project what i
* [Nonoss117](https://github.com/benphelps/homepage/commits?author=Nonoss117) - French Translation
* [quod](https://github.com/benphelps/homepage/commits?author=quod) - Fixed Typos
* [schklom](https://github.com/benphelps/homepage/commits?author=schklom) - ARM64, ARMv7 and ARMv6
* [xicopitz](https://github.com/benphelps/homepage/commits?author=xicopitz) - Gotify Integration
* [xicopitz](https://github.com/benphelps/homepage/commits?author=xicopitz) - Gotify & Prowlarr Integration

@ -7,6 +7,7 @@ const nextConfig = {
domains: ["cdn.jsdelivr.net"],
unoptimized: true,
},
experimental: { images: { allowFutureImage: true } }
};
module.exports = nextConfig;

@ -10,7 +10,8 @@
"resources": {
"total": "Gesamt",
"free": "Frei",
"used": "Gebraucht"
"used": "Gebraucht",
"load": "Load"
},
"docker": {
"rx": "Rx",
@ -114,5 +115,12 @@
"apps": "Applications",
"clients": "Clients",
"messages": "Messages"
},
"prowlarr": {
"enableIndexers": "Indexers",
"numberOfGrabs": "Grabs",
"numberOfQueries": "Queries",
"numberOfFailGrabs": "Fail Grabs",
"numberOfFailQueries": "Fail Queries"
}
}

@ -27,7 +27,8 @@
"resources": {
"total": "Total",
"free": "Free",
"used": "Used"
"used": "Used",
"load": "Load"
},
"docker": {
"rx": "RX",
@ -131,5 +132,12 @@
"apps": "Applications",
"clients": "Clients",
"messages": "Messages"
},
"prowlarr":{
"enableIndexers": "Indexers",
"numberOfGrabs": "Grabs",
"numberOfQueries": "Queries",
"numberOfFailGrabs": "Fail Grabs",
"numberOfFailQueries": "Fail Queries"
}
}

@ -10,7 +10,8 @@
"resources": {
"total": "Total",
"free": "Libre",
"used": "Usado"
"used": "Usado",
"load": "Load"
},
"docker": {
"rx": "Recibido",
@ -47,9 +48,9 @@
"movies": "Películas"
},
"readarr": {
"wanted": "Wanted",
"queued": "Queued",
"books": "Books"
"wanted": "Más deseado",
"queued": "Puesto en cola",
"books": "Libros"
},
"ombi": {
"pending": "Pendiente",
@ -98,21 +99,28 @@
"available": "Disponible"
},
"sabnzbd": {
"rate": "Rate",
"queue": "Queue",
"timeleft": "Time Left"
"rate": "Tasa de descarga",
"queue": "Puesto en cola",
"timeleft": "Tiempo Restante"
},
"nzbget": {
"rate": "Rate",
"remaining": "Remaining",
"downloaded": "Downloaded"
"rate": "Tasa de descarga",
"remaining": "Restante",
"downloaded": "Descargado"
},
"coinmarketcap": {
"configure": "Configure one or more crypto currencies to track"
"configure": "Configurar una o varias criptomonedas para su seguimiento"
},
"gotify": {
"apps": "Applications",
"clients": "Clients",
"messages": "Messages"
"apps": "Aplicaciones",
"clients": "Clientes",
"messages": "Mensajes"
},
"prowlarr": {
"enableIndexers": "Indexers",
"numberOfGrabs": "Grabs",
"numberOfQueries": "Queries",
"numberOfFailGrabs": "Fail Grabs",
"numberOfFailQueries": "Fail Queries"
}
}

@ -10,7 +10,8 @@
"resources": {
"total": "Total",
"free": "Libre",
"used": "Utilisée"
"used": "Utilisé",
"load": "Charge"
},
"docker": {
"rx": "Rx",
@ -32,53 +33,53 @@
"no_active": "Aucun flux actif"
},
"rutorrent": {
"active": "Active",
"active": "Actif",
"upload": "Téléverser",
"download": "Télécharger"
},
"sonarr": {
"wanted": "Recherchée",
"wanted": "Demandé",
"queued": "En queue",
"series": "Séries"
},
"radarr": {
"wanted": "Recherchée",
"wanted": "Demandé",
"queued": "En queue",
"movies": "Films"
},
"readarr": {
"wanted": "Wanted",
"queued": "Queued",
"books": "Books"
"wanted": "Demandé",
"queued": "En Queue",
"books": "Livres"
},
"ombi": {
"pending": "En attente",
"approved": "Approuvée",
"approved": "Validé",
"available": "Disponible"
},
"jellyseerr": {
"pending": "En attente",
"approved": "Approuvée",
"approved": "Validé",
"available": "Disponible"
},
"pihole": {
"queries": "Requêtes",
"blocked": "Bloquée",
"blocked": "Bloqué",
"gravity": "La gravité"
},
"speedtest": {
"upload": "Téléversement",
"download": "Téléchargement",
"ping": "Ping-ping"
"ping": "Ping"
},
"portainer": {
"running": "Fonctionnement",
"running": "Démarré",
"stopped": "Arrêté",
"total": "Total"
},
"traefik": {
"routers": "Routeurs",
"services": "Prestations de service",
"services": "Services",
"middleware": "Middleware"
},
"npm": {
@ -105,25 +106,32 @@
},
"overseerr": {
"pending": "En attente",
"approved": "Approuvée",
"approved": "Validé",
"available": "Disponible"
},
"sabnzbd": {
"rate": "Rate",
"rate": "Taux",
"queue": "Queue",
"timeleft": "Time Left"
"timeleft": "Temps restant"
},
"nzbget": {
"remaining": "Remaining",
"downloaded": "Downloaded",
"rate": "Rate"
"remaining": "Restant",
"downloaded": "Téléchargé",
"rate": "Évaluer"
},
"coinmarketcap": {
"configure": "Configure one or more crypto currencies to track"
"configure": "Configurer une ou plusieurs crypto-monnaies à suivre"
},
"gotify": {
"apps": "Applications",
"clients": "Clients",
"messages": "Messages"
},
"prowlarr": {
"enableIndexers": "Indexeurs",
"numberOfGrabs": "Capture",
"numberOfQueries": "Demandes",
"numberOfFailGrabs": "Capture échouée",
"numberOfFailQueries": "Demande échouée"
}
}

@ -44,7 +44,8 @@
"resources": {
"total": "Totale",
"free": "Libero",
"used": "In utilizzo"
"used": "In utilizzo",
"load": "Load"
},
"rutorrent": {
"active": "Attivo",
@ -114,5 +115,12 @@
"apps": "Applications",
"clients": "Clients",
"messages": "Messages"
},
"prowlarr": {
"enableIndexers": "Indexers",
"numberOfGrabs": "Grabs",
"numberOfQueries": "Queries",
"numberOfFailGrabs": "Fail Grabs",
"numberOfFailQueries": "Fail Queries"
}
}

@ -10,7 +10,8 @@
"resources": {
"total": "Totalt",
"free": "Ledig",
"used": "Brukt"
"used": "Brukt",
"load": "Last inn"
},
"docker": {
"rx": "Mottatt",
@ -23,13 +24,13 @@
"playing": "Spiller",
"transcoding": "Transkoding",
"bitrate": "Bitrate",
"no_active": "No Active Streams"
"no_active": "Ingen aktive strømmer"
},
"tautulli": {
"playing": "Spiller",
"transcoding": "Transkoding",
"bitrate": "Bitrate",
"no_active": "No Active Streams"
"no_active": "Ingen aktive strømmer"
},
"rutorrent": {
"active": "Aktiv",
@ -93,26 +94,33 @@
"current": "Nåværende posisjon"
},
"overseerr": {
"pending": "Pending",
"approved": "Approved",
"available": "Available"
"pending": "Venter",
"approved": "Godkjent",
"available": "Tilgjengelig"
},
"sabnzbd": {
"rate": "Rate",
"queue": "Queue",
"timeleft": "Time Left"
"rate": "Takt",
"queue": "",
"timeleft": "Gjenstående tid"
},
"nzbget": {
"rate": "Rate",
"downloaded": "Downloaded",
"remaining": "Remaining"
"rate": "Takt",
"downloaded": "Nedlastet",
"remaining": "Gjenstående"
},
"coinmarketcap": {
"configure": "Configure one or more crypto currencies to track"
"configure": "Sett opp én eller flere kryptovalutaer å holde øye med"
},
"gotify": {
"apps": "Applications",
"clients": "Clients",
"messages": "Messages"
"apps": "Programmer",
"clients": "Klienter",
"messages": "Meldinger"
},
"prowlarr": {
"enableIndexers": "Indekserere",
"numberOfGrabs": "Hentninger",
"numberOfQueries": "Spørringer",
"numberOfFailGrabs": "Mislykkede hentinger",
"numberOfFailQueries": "Mislykkede spørringer"
}
}

@ -7,7 +7,8 @@
"resources": {
"total": "Totaal",
"free": "Vrij",
"used": "Gebruikt"
"used": "Gebruikt",
"load": "Load"
},
"docker": {
"rx": "RX",
@ -114,5 +115,12 @@
"apps": "Applications",
"clients": "Clients",
"messages": "Messages"
},
"prowlarr": {
"enableIndexers": "Indexers",
"numberOfGrabs": "Grabs",
"numberOfQueries": "Queries",
"numberOfFailGrabs": "Fail Grabs",
"numberOfFailQueries": "Fail Queries"
}
}

@ -10,7 +10,8 @@
"resources": {
"total": "Total",
"free": "Livre",
"used": "Usada"
"used": "Usada",
"load": "Load"
},
"docker": {
"rx": "Rx",
@ -125,5 +126,12 @@
"apps": "Aplicações",
"clients": "Clientes",
"messages": "Mensagens"
},
"prowlarr": {
"enableIndexers": "Indexers",
"numberOfGrabs": "Grabs",
"numberOfQueries": "Queries",
"numberOfFailGrabs": "Fail Grabs",
"numberOfFailQueries": "Fail Queries"
}
}

@ -10,7 +10,8 @@
"resources": {
"total": "Общий",
"free": "Свободно",
"used": "Использовано"
"used": "Использовано",
"load": "Load"
},
"docker": {
"rx": "Rx",
@ -114,5 +115,12 @@
"apps": "Applications",
"clients": "Clients",
"messages": "Messages"
},
"prowlarr": {
"enableIndexers": "Indexers",
"numberOfGrabs": "Grabs",
"numberOfQueries": "Queries",
"numberOfFailGrabs": "Fail Grabs",
"numberOfFailQueries": "Fail Queries"
}
}

@ -10,7 +10,8 @@
"resources": {
"total": "Tổng",
"free": "Dư",
"used": "Đã dùng"
"used": "Đã dùng",
"load": "Load"
},
"docker": {
"rx": "RX",
@ -114,5 +115,12 @@
"apps": "Applications",
"clients": "Clients",
"messages": "Messages"
},
"prowlarr": {
"numberOfFailGrabs": "Fail Grabs",
"enableIndexers": "Indexers",
"numberOfGrabs": "Grabs",
"numberOfQueries": "Queries",
"numberOfFailQueries": "Fail Queries"
}
}

@ -8,31 +8,32 @@
"placeholder": "搜索…"
},
"resources": {
"total": "全部的",
"free": "自由的",
"used": "用过的"
"total": "共",
"free": "空闲",
"used": "已用",
"load": "Load"
},
"docker": {
"rx": "rx",
"tx": "TX",
"mem": "mem",
"cpu": "中央处理器",
"rx": "接收",
"tx": "发送",
"mem": "内存",
"cpu": "处理器",
"offline": "离线"
},
"emby": {
"playing": "",
"playing": "正在播放",
"transcoding": "转码",
"bitrate": "比特率",
"no_active": "No Active Streams"
"no_active": "暂无播放"
},
"tautulli": {
"playing": "",
"playing": "正在播放",
"transcoding": "转码",
"bitrate": "比特率",
"no_active": "No Active Streams"
"no_active": "暂无播放"
},
"rutorrent": {
"active": "积极的",
"active": "活动中",
"upload": "上传",
"download": "下载"
},
@ -42,18 +43,18 @@
"series": "系列"
},
"radarr": {
"wanted": "通缉",
"queued": "队",
"wanted": "订阅",
"queued": "",
"movies": "电影"
},
"readarr": {
"wanted": "Wanted",
"queued": "Queued",
"books": "Books"
"wanted": "订阅",
"queued": "队列",
"books": "书籍"
},
"ombi": {
"pending": "待办的",
"approved": "得到正式认可的",
"approved": "已批准",
"available": "可用的"
},
"jellyseerr": {
@ -72,9 +73,9 @@
"ping": "ping"
},
"portainer": {
"running": "跑步",
"stopped": "停了下来",
"total": "全部的"
"running": "运行中",
"stopped": "已停止",
"total": "总计"
},
"traefik": {
"routers": "路由器",
@ -87,32 +88,39 @@
"total": "全部的"
},
"weather": {
"current": "Current Location",
"allow": "Click to allow",
"updating": "Updating",
"wait": "Please wait"
"current": "当前位置",
"allow": "点击并允许",
"updating": "更新中",
"wait": "请等待"
},
"overseerr": {
"pending": "Pending",
"approved": "Approved",
"available": "Available"
"pending": "待办",
"approved": "已批准",
"available": "可用"
},
"sabnzbd": {
"rate": "Rate",
"queue": "Queue",
"timeleft": "Time Left"
"rate": "速率",
"queue": "队列",
"timeleft": "剩余时间"
},
"nzbget": {
"rate": "Rate",
"remaining": "Remaining",
"downloaded": "Downloaded"
"rate": "速率",
"remaining": "剩余",
"downloaded": "下载"
},
"coinmarketcap": {
"configure": "Configure one or more crypto currencies to track"
"configure": "配置一个或多个需要追踪的加密"
},
"gotify": {
"apps": "Applications",
"clients": "Clients",
"messages": "Messages"
"apps": "应用",
"clients": "客户端",
"messages": "信息"
},
"prowlarr": {
"enableIndexers": "Indexers",
"numberOfGrabs": "Grabs",
"numberOfQueries": "Queries",
"numberOfFailGrabs": "Fail Grabs",
"numberOfFailQueries": "Fail Queries"
}
}

@ -6,7 +6,7 @@ export default function Item({ bookmark }) {
<button
type="button"
onClick={() => window.open(bookmark.href, "_blank").focus()}
className="w-full text-left mb-3 cursor-pointer rounded-md font-medium text-theme-700 hover:text-theme-700 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-black/10 dark:shadow-black/20 bg-white/50 hover:bg-theme-300/10 dark:bg-white/10 dark:hover:bg-white/20 backdrop-blur-md"
className="w-full text-left mb-3 cursor-pointer rounded-md font-medium text-theme-700 hover:text-theme-700 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-black/10 dark:shadow-black/20 bg-white/50 hover:bg-theme-300/10 dark:bg-white/10 dark:hover:bg-white/20"
>
<div className="flex">
<div className="flex-shrink-0 flex items-center justify-center w-11 bg-theme-500/10 dark:bg-theme-900/50 text-theme-700 hover:text-theme-700 dark:text-theme-200 text-sm font-medium rounded-l-md">

@ -36,7 +36,7 @@ export default function Item({ service }) {
<div
className={`${
hasLink ? "cursor-pointer " : " "
}transition-all h-15 mb-3 p-1 rounded-md font-medium text-theme-700 hover:text-theme-700/70 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-black/10 dark:shadow-black/20 bg-white/50 hover:bg-theme-300/20 dark:bg-white/10 dark:hover:bg-white/20 backdrop-blur-md`}
}transition-all h-15 mb-3 p-1 rounded-md font-medium text-theme-700 hover:text-theme-700/70 dark:text-theme-200 dark:hover:text-theme-300 shadow-md shadow-black/10 dark:shadow-black/20 bg-white/50 hover:bg-theme-300/20 dark:bg-white/10 dark:hover:bg-white/20`}
>
<div className="flex select-none">
{service.icon &&

@ -21,6 +21,7 @@ import Npm from "./widgets/service/npm";
import Tautulli from "./widgets/service/tautulli";
import CoinMarketCap from "./widgets/service/coinmarketcap";
import Gotify from "./widgets/service/gotify";
import Prowlarr from "./widgets/service/prowlarr";
const widgetMappings = {
docker: Docker,
@ -44,6 +45,7 @@ const widgetMappings = {
npm: Npm,
tautulli: Tautulli,
gotify: Gotify,
prowlarr: Prowlarr
};
export default function Widget({ service }) {

@ -0,0 +1,55 @@
import useSWR from "swr";
import { useTranslation } from "react-i18next";
import Widget from "../widget";
import Block from "../block";
import { formatApiUrl } from "utils/api-helpers";
export default function Prowlarr({ service }) {
const { t } = useTranslation();
const config = service.widget;
const { data: indexersData, error: indexersError } = useSWR(formatApiUrl(config, "indexer"));
const { data: grabsData, error: grabsError } = useSWR(formatApiUrl(config, "indexerstats"));
if (indexersError || grabsError) {
return <Widget error={t("widget.api_error")} />;
}
if (!indexersData || !grabsData) {
return (
<Widget>
<Block label={t("prowlarr.enableIndexers")} />
<Block label={t("prowlarr.numberOfGrabs")} />
<Block label={t("prowlarr.numberOfQueries")} />
<Block label={t("prowlarr.numberOfFailGrabs")} />
<Block label={t("prowlarr.numberOfFailQueries")} />
</Widget>
);
}
const indexers = indexersData?.filter((indexer) => indexer.enable === true);
let numberOfGrabs = 0
let numberOfQueries = 0
let numberOfFailedGrabs = 0
let numberOfFailedQueries = 0
grabsData?.indexers?.forEach(element => {
numberOfGrabs += element.numberOfGrabs;
numberOfQueries += element.numberOfQueries;
numberOfFailedGrabs += numberOfFailedGrabs + element.numberOfFailedGrabs;
numberOfFailedQueries += numberOfFailedQueries + element.numberOfFailedQueries;
});
return (
<Widget>
<Block label={t("prowlarr.enableIndexers")} value={indexers.length} />
<Block label={t("prowlarr.numberOfGrabs")} value={numberOfGrabs} />
<Block label={t("prowlarr.numberOfQueries")} value={numberOfQueries} />
<Block label={t("prowlarr.numberOfFailGrabs")} value={numberOfFailedGrabs} />
<Block label={t("prowlarr.numberOfFailQueries")} value={numberOfFailedQueries} />
</Widget>
);
}

@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
import UsageBar from "./usage-bar";
export default function Cpu() {
export default function Cpu({ expanded }) {
const { t } = useTranslation();
const { data, error } = useSWR(`/api/widgets/resources?type=cpu`, {
@ -39,11 +39,29 @@ export default function Cpu() {
return (
<div className="flex-none flex flex-row items-center mr-3 py-1.5">
<FiCpu className="text-theme-800 dark:text-theme-200 w-5 h-5" />
<div className="flex flex-col ml-3 text-left font-mono min-w-[80px]">
<div className="text-theme-800 dark:text-theme-200 text-xs">
{t("common.number", { value: data.cpu.usage, style: "unit", unit: "percent", maximumFractionDigits: 0 })}{" "}
{t("docker.cpu")}
<div className="flex flex-col ml-3 text-left min-w-[85px]">
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
<div className="pl-0.5">
{t("common.number", {
value: data.cpu.usage,
style: "unit",
unit: "percent",
maximumFractionDigits: 0,
})}
</div>
<div className="pr-1">{t("docker.cpu")}</div>
</div>
{expanded && (
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
<div className="pl-0.5">
{t("common.number", {
value: data.cpu.load,
maximumFractionDigits: 2,
})}
</div>
<div className="pr-1">{t("resources.load")}</div>
</div>
)}
<UsageBar percent={percent} />
</div>
</div>

@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
import UsageBar from "./usage-bar";
export default function Disk({ options }) {
export default function Disk({ options, expanded }) {
const { t } = useTranslation();
const { data, error } = useSWR(`/api/widgets/resources?type=disk&target=${options.disk}`, {
@ -37,15 +37,19 @@ export default function Disk({ options }) {
const percent = Math.round((data.drive.usedGb / data.drive.totalGb) * 100);
return (
<div className="flex-none flex flex-row items-center mr-3 py-1.5 group">
<div className="flex-none flex flex-row items-center mr-3 py-1.5">
<FiHardDrive className="text-theme-800 dark:text-theme-200 w-5 h-5" />
<div className="flex flex-col ml-3 text-left min-w-[80px]">
<span className="text-theme-800 dark:text-theme-200 text-xs group-hover:hidden">
{t("common.bytes", { value: data.drive.freeGb * 1024 * 1024 * 1024 })} {t("resources.free")}
</span>
<span className="text-theme-800 dark:text-theme-200 text-xs hidden group-hover:block">
{t("common.bytes", { value: data.drive.totalGb * 1024 * 1024 * 1024 })} {t("resources.total")}
<div className="flex flex-col ml-3 text-left min-w-[85px]">
<span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
<div className="pl-0.5">{t("common.bytes", { value: data.drive.freeGb * 1024 * 1024 * 1024 })}</div>
<div className="pr-1">{t("resources.free")}</div>
</span>
{expanded && (
<span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
<div className="pl-0.5">{t("common.bytes", { value: data.drive.totalGb * 1024 * 1024 * 1024 })}</div>
<div className="pr-1">{t("resources.total")}</div>
</span>
)}
<UsageBar percent={percent} />
</div>
</div>

@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
import UsageBar from "./usage-bar";
export default function Memory() {
export default function Memory({ expanded }) {
const { t } = useTranslation();
const { data, error } = useSWR(`/api/widgets/resources?type=memory`, {
@ -37,15 +37,27 @@ export default function Memory() {
const percent = Math.round((data.memory.usedMemMb / data.memory.totalMemMb) * 100);
return (
<div className="flex-none flex flex-row items-center mr-3 py-1.5 group">
<div className="flex-none flex flex-row items-center mr-3 py-1.5">
<FaMemory className="text-theme-800 dark:text-theme-200 w-5 h-5" />
<div className="flex flex-col ml-3 text-left min-w-[80px]">
<span className="text-theme-800 dark:text-theme-200 text-xs group-hover:hidden">
{t("common.bytes", { value: data.memory.freeMemMb * 1024 * 1024 })} {t("resources.free")}
</span>
<span className="text-theme-800 dark:text-theme-200 text-xs hidden group-hover:block">
{t("common.bytes", { value: data.memory.usedMemMb * 1024 * 1024 })} {t("resources.used")}
<div className="flex flex-col ml-3 text-left min-w-[85px]">
<span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
<div className="pl-0.5">
{t("common.bytes", { value: data.memory.freeMemMb * 1024 * 1024, maximumFractionDigits: 0, binary: true })}
</div>
<div className="pr-1">{t("resources.free")}</div>
</span>
{expanded && (
<span className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
<div className="pl-0.5">
{t("common.bytes", {
value: data.memory.totalMemMb * 1024 * 1024,
maximumFractionDigits: 0,
binary: true,
})}
</div>
<div className="pr-1">{t("resources.total")}</div>
</span>
)}
<UsageBar percent={percent} />
</div>
</div>

@ -3,14 +3,16 @@ import Cpu from "./cpu";
import Memory from "./memory";
export default function Resources({ options }) {
console.log(options);
const { expanded } = options;
return (
<div className="flex flex-col max-w:full sm:basis-auto self-center m-auto flex-wrap">
<div className="flex flex-row self-center flex-wrap justify-between">
{options.cpu && <Cpu />}
{options.memory && <Memory />}
{options.cpu && <Cpu expanded={expanded} />}
{options.memory && <Memory expanded={expanded} />}
{Array.isArray(options.disk)
? options.disk.map((disk) => <Disk key={disk} options={{ disk }} />)
: options.disk && <Disk options={options} />}
? options.disk.map((disk) => <Disk key={disk} options={{ disk }} expanded={expanded} />)
: options.disk && <Disk options={options} expanded={expanded} />}
</div>
{options.label && (
<div className="ml-6 pt-1 text-center text-theme-800 dark:text-theme-200 text-xs">{options.label}</div>

@ -1,6 +1,6 @@
export default function UsageBar({ percent }) {
return (
<div className="mt-0.5 w-full bg-theme-800/30 rounded-full h-1 dark:bg-white/20 backdrop-blur-md">
<div className="mt-0.5 w-full bg-theme-800/30 rounded-full h-1 dark:bg-white/20">
<div
className="bg-theme-800/70 h-1 rounded-full dark:bg-white/50"
style={{

@ -62,8 +62,7 @@ export default function Search({ options }) {
bg-white/50 dark:bg-white/10
focus:ring-theme-500 dark:focus:ring-white/50
focus:border-theme-500 dark:focus:border-white/50
border border-theme-300 dark:border-theme-200/50
backdrop-blur-md"
border border-theme-300 dark:border-theme-200/50"
placeholder={t("search.placeholder")}
onChange={(s) => setQuery(s.currentTarget.value)}
required

@ -24,6 +24,7 @@ const serviceProxyHandlers = {
overseerr: credentialedProxyHandler,
ombi: credentialedProxyHandler,
coinmarketcap: credentialedProxyHandler,
prowlarr: credentialedProxyHandler,
// super specific handlers
rutorrent: rutorrentProxyHandler,
nzbget: nzbgetProxyHandler,

@ -18,6 +18,7 @@ const formats = {
sabnzbd: `{url}/api/?apikey={key}&output=json&mode={endpoint}`,
coinmarketcap: `https://pro-api.coinmarketcap.com/{endpoint}`,
gotify: `{url}/{endpoint}`,
prowlarr: `{url}/api/v1/{endpoint}`,
};
export function formatApiCall(api, args) {

Loading…
Cancel
Save