mirror of
https://github.com/gethomepage/homepage.git
synced 2025-12-06 21:57:48 +01:00
Compare commits
161 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d16fd0cd05 | ||
|
|
6dc6ee54ea | ||
|
|
5b515822d3 | ||
|
|
f81d754044 | ||
|
|
483c5cf87a | ||
|
|
6b634b4ef5 | ||
|
|
f58ae97c6e | ||
|
|
5deb58f092 | ||
|
|
55cd4c5ada | ||
|
|
27a7adc282 | ||
|
|
6b72b60b5e | ||
|
|
c2d6b9738f | ||
|
|
44dbff308a | ||
|
|
c224a26bc8 | ||
|
|
a1e9afae85 | ||
|
|
bca6340ded | ||
|
|
f1e7df394e | ||
|
|
a2bbcce69e | ||
|
|
de8747c9f5 | ||
|
|
0b8ce67e10 | ||
|
|
0de4d00d83 | ||
|
|
0b47e083ea | ||
|
|
2e0b55bd37 | ||
|
|
ee5bb5364b | ||
|
|
bb3fd88a90 | ||
|
|
ac5dfe5a9c | ||
|
|
0bc65e4345 | ||
|
|
92b80d1e46 | ||
|
|
dab627e248 | ||
|
|
70a77718db | ||
|
|
9a81c9a418 | ||
|
|
6ef05b8118 | ||
|
|
6fd187c54c | ||
|
|
abf65caba7 | ||
|
|
5470c68cb4 | ||
|
|
f0bca41c5e | ||
|
|
c985c4788b | ||
|
|
2df5e8d791 | ||
|
|
2e8ea3b0d8 | ||
|
|
8a2a624956 | ||
|
|
b1aa093f5d | ||
|
|
216a67fc12 | ||
|
|
85d9b2fc8a | ||
|
|
65454014ef | ||
|
|
4e23a931cd | ||
|
|
3446c9b2a1 | ||
|
|
ccda10fca4 | ||
|
|
a14dfb9360 | ||
|
|
f3e52f0bdf | ||
|
|
f7a666e8c5 | ||
|
|
1feaae7291 | ||
|
|
112cbaf813 | ||
|
|
5263a66fbe | ||
|
|
d1971986ea | ||
|
|
988f3dfdd5 | ||
|
|
95794dc837 | ||
|
|
8bb716e1c5 | ||
|
|
ec3faa31a5 | ||
|
|
09d7b8f5ed | ||
|
|
60fcbbfeaf | ||
|
|
6d1c9263b7 | ||
|
|
5bd1dd71f6 | ||
|
|
4fcc8f004d | ||
|
|
a11f22cd49 | ||
|
|
d79bf7ef80 | ||
|
|
a88a803d38 | ||
|
|
23b59dc542 | ||
|
|
751167fb17 | ||
|
|
d1336c5b95 | ||
|
|
18765f7b4d | ||
|
|
04f2d2a0e4 | ||
|
|
64a6af7bf6 | ||
|
|
158bb0ae41 | ||
|
|
c3e14a0593 | ||
|
|
3e177ec39d | ||
|
|
b7d9124228 | ||
|
|
67ed9b294b | ||
|
|
931947bffc | ||
|
|
de9ca09e70 | ||
|
|
7ef294291a | ||
|
|
e44972ec44 | ||
|
|
b7a45512cf | ||
|
|
4621724fc6 | ||
|
|
fb9c673130 | ||
|
|
15b08cdd44 | ||
|
|
76db303368 | ||
|
|
d5d0313b21 | ||
|
|
f419b3753a | ||
|
|
705258d30d | ||
|
|
413b7a7203 | ||
|
|
d3a8d27276 | ||
|
|
725c1b7da4 | ||
|
|
45767d4b27 | ||
|
|
0e4d1da1cc | ||
|
|
55f9d77054 | ||
|
|
757ef40afc | ||
|
|
ee95c23c3d | ||
|
|
93445a2831 | ||
|
|
3bef3dd6c6 | ||
|
|
db46931246 | ||
|
|
7ca7a9cc58 | ||
|
|
f7d5582f57 | ||
|
|
82486d1e13 | ||
|
|
80cc9fc984 | ||
|
|
17b21eb029 | ||
|
|
0a2528d476 | ||
|
|
db9afd4d7c | ||
|
|
5f116226cf | ||
|
|
41ce91cf81 | ||
|
|
a6599b1ea0 | ||
|
|
aed29762fd | ||
|
|
411ca43729 | ||
|
|
5401896829 | ||
|
|
92a5e20c7e | ||
|
|
c6677d55da | ||
|
|
1168b301dd | ||
|
|
e1d835259d | ||
|
|
4b38db6380 | ||
|
|
d7f06dbfbb | ||
|
|
86485740eb | ||
|
|
97550a67b7 | ||
|
|
e8d50201a5 | ||
|
|
39e276644f | ||
|
|
d72cbb79f5 | ||
|
|
0a260db335 | ||
|
|
64a77ce04f | ||
|
|
5f33f061cc | ||
|
|
7606763cc6 | ||
|
|
d73430b299 | ||
|
|
0ab988119d | ||
|
|
6d97ac77f6 | ||
|
|
cd2b9d2e0a | ||
|
|
32ef47e6d2 | ||
|
|
cb3c20ecbd | ||
|
|
12ed730897 | ||
|
|
aaa1f76176 | ||
|
|
fb2492e577 | ||
|
|
e7b0fc1419 | ||
|
|
7dd0b0e4eb | ||
|
|
6b55a50b05 | ||
|
|
4f38c0ee80 | ||
|
|
95dcb8802a | ||
|
|
9188f5cdd9 | ||
|
|
645cf211dd | ||
|
|
c720df0805 | ||
|
|
b7722ed333 | ||
|
|
fb883c7b27 | ||
|
|
f51e755216 | ||
|
|
146326f427 | ||
|
|
ccc1229098 | ||
|
|
f6b6c64b93 | ||
|
|
91d8e56471 | ||
|
|
702dbd8a82 | ||
|
|
5e722b4d11 | ||
|
|
712fbb53c7 | ||
|
|
88c437562b | ||
|
|
537fd0ac21 | ||
|
|
59cb7baf0b | ||
|
|
94af8044f1 | ||
|
|
5d8b937e9c | ||
|
|
99b2ba8944 |
@@ -47,7 +47,7 @@
|
||||
- Service Integration
|
||||
- Sonarr, Radarr, Readarr, Prowlarr, Bazarr, Lidarr, Emby, Jellyfin, Tautulli (Plex)
|
||||
- Ombi, Overseerr, Jellyseerr, Jackett, NZBGet, SABnzbd, ruTorrent, Transmission, qBittorrent
|
||||
- Portainer, Traefik, Speedtest Tracker, PiHole, AdGuard Home, Nginx Proxy Manager, Gotify, Syncthing Relay Server, Authentic, Proxmox
|
||||
- Portainer, Traefik, Speedtest Tracker, PiHole, AdGuard Home, Nginx Proxy Manager, Gotify, Syncthing Relay Server, Authentik, Proxmox
|
||||
- Information Providers
|
||||
- Coin Market Cap, Mastodon
|
||||
- Information & Utility Widgets
|
||||
|
||||
@@ -98,20 +98,21 @@ module.exports = {
|
||||
);
|
||||
|
||||
i18next.services.formatter.add("rate", (value, lng, options) => {
|
||||
if (value === 0) return "0 Bps";
|
||||
|
||||
const bits = options.bits ? value : value / 8;
|
||||
const k = 1024;
|
||||
const k = options.binary ? 1024 : 1000;
|
||||
const sizes = options.bits ? (options.binary ? BIBIT_UNITS : BIT_UNITS) : (options.binary ? BIBYTE_UNITS : BYTE_UNITS);
|
||||
|
||||
if (value === 0) return `0 ${sizes[0]}/s`;
|
||||
|
||||
const dm = options.decimals ? options.decimals : 0;
|
||||
const sizes = ["Bps", "KiBps", "MiBps", "GiBps", "TiBps", "PiBps", "EiBps", "ZiBps", "YiBps"];
|
||||
|
||||
const i = Math.floor(Math.log(bits) / Math.log(k));
|
||||
const i = options.binary ? 2 : Math.floor(Math.log(value) / Math.log(k));
|
||||
|
||||
const formatted = new Intl.NumberFormat(lng, { maximumFractionDigits: dm, minimumFractionDigits: dm }).format(
|
||||
parseFloat(bits / k ** i)
|
||||
parseFloat(value / k ** i)
|
||||
);
|
||||
|
||||
return `${formatted} ${sizes[i]}`;
|
||||
return `${formatted} ${sizes[i]}/s`;
|
||||
});
|
||||
|
||||
i18next.services.formatter.add("percent", (value, lng, options) =>
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"seed": "Seed",
|
||||
"download": "Download",
|
||||
"upload": "Upload"
|
||||
},
|
||||
"tdarr": {
|
||||
"saved": "Saved",
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
"missing_type": "Falta el tipus de widget: {{type}}",
|
||||
"api_error": "Error d'API",
|
||||
"status": "Estat",
|
||||
"information": "Information",
|
||||
"information": "Informació",
|
||||
"url": "URL",
|
||||
"raw_error": "Raw Error",
|
||||
"response_data": "Response Data"
|
||||
"raw_error": "Error sense processar",
|
||||
"response_data": "Dades de resposta"
|
||||
},
|
||||
"weather": {
|
||||
"allow": "Feu clic per permetre",
|
||||
@@ -20,8 +20,8 @@
|
||||
"transmission": {
|
||||
"seed": "Llavors",
|
||||
"download": "Descàrrega",
|
||||
"upload": "Càrrega",
|
||||
"leech": "Companys"
|
||||
"upload": "Pujada",
|
||||
"leech": "Company"
|
||||
},
|
||||
"sonarr": {
|
||||
"wanted": "Volgut",
|
||||
@@ -30,13 +30,13 @@
|
||||
},
|
||||
"speedtest": {
|
||||
"ping": "Ping",
|
||||
"upload": "Càrrega",
|
||||
"upload": "Pujada",
|
||||
"download": "Descàrrega"
|
||||
},
|
||||
"resources": {
|
||||
"total": "Total",
|
||||
"free": "Lliure",
|
||||
"used": "Usat",
|
||||
"used": "Utilitzat",
|
||||
"load": "Càrrega",
|
||||
"cpu": "CPU"
|
||||
},
|
||||
@@ -47,13 +47,13 @@
|
||||
"cpu": "Processador",
|
||||
"offline": "Fora de línia",
|
||||
"error": "Error",
|
||||
"unknown": "Unknown"
|
||||
"unknown": "Desconegut"
|
||||
},
|
||||
"emby": {
|
||||
"playing": "Reproduint",
|
||||
"transcoding": "Transcodificant",
|
||||
"bitrate": "Taxa de bits",
|
||||
"no_active": "Sense transmissions actives"
|
||||
"no_active": "Sense reproduccions actives"
|
||||
},
|
||||
"tautulli": {
|
||||
"playing": "Reproduint",
|
||||
@@ -73,14 +73,14 @@
|
||||
},
|
||||
"rutorrent": {
|
||||
"active": "Actiu",
|
||||
"upload": "Càrrega",
|
||||
"upload": "Pujada",
|
||||
"download": "Descàrrega"
|
||||
},
|
||||
"radarr": {
|
||||
"wanted": "Volgut",
|
||||
"queued": "En cua",
|
||||
"movies": "Pel·lícules",
|
||||
"missing": "Missing"
|
||||
"missing": "Faltant"
|
||||
},
|
||||
"readarr": {
|
||||
"wanted": "Volgut",
|
||||
@@ -101,7 +101,7 @@
|
||||
"pending": "Pendent",
|
||||
"approved": "Aprovat",
|
||||
"available": "Disponible",
|
||||
"processing": "Processing"
|
||||
"processing": "Processant"
|
||||
},
|
||||
"pihole": {
|
||||
"queries": "Consultes",
|
||||
@@ -163,8 +163,8 @@
|
||||
},
|
||||
"qbittorrent": {
|
||||
"download": "Descàrrega",
|
||||
"upload": "Càrrega",
|
||||
"leech": "Companys",
|
||||
"upload": "Pujada",
|
||||
"leech": "Company",
|
||||
"seed": "Llavors"
|
||||
},
|
||||
"mastodon": {
|
||||
@@ -184,26 +184,26 @@
|
||||
"failedLoginsLast24H": "Errors d'inici de sessió (24h)"
|
||||
},
|
||||
"proxmox": {
|
||||
"vms": "VMs",
|
||||
"vms": "Màquines Virtuals",
|
||||
"mem": "Memòria",
|
||||
"cpu": "Processador",
|
||||
"lxc": "LXC"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Usuaris",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Días",
|
||||
"uptime": "Temps actiu",
|
||||
"days": "Dies",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"lan_users": "Usuaris LAN",
|
||||
"wlan_users": "Usuaris WLAN",
|
||||
"up": "ACTIU",
|
||||
"down": "INACTIU",
|
||||
"wait": "Si us plau, espereu",
|
||||
"lan": "LAN",
|
||||
"wlan": "WLAN",
|
||||
"devices": "Devices",
|
||||
"lan_devices": "LAN Devices",
|
||||
"wlan_devices": "WLAN Devices"
|
||||
"devices": "Dispositius",
|
||||
"lan_devices": "Dispositius LAN",
|
||||
"wlan_devices": "Dispositius WLAN"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Transmissions actives",
|
||||
@@ -216,119 +216,119 @@
|
||||
"wait": "Si us plau, espereu"
|
||||
},
|
||||
"changedetectionio": {
|
||||
"totalObserved": "Total Observed",
|
||||
"diffsDetected": "Diffs Detected"
|
||||
"totalObserved": "Total d'observats",
|
||||
"diffsDetected": "Diferències detectades"
|
||||
},
|
||||
"wmo": {
|
||||
"66-day": "Freezing Rain",
|
||||
"95-day": "Thunderstorm",
|
||||
"95-night": "Thunderstorm",
|
||||
"96-day": "Thunderstorm With Hail",
|
||||
"0-day": "Sunny",
|
||||
"0-night": "Clear",
|
||||
"1-day": "Mainly Sunny",
|
||||
"1-night": "Mainly Clear",
|
||||
"2-day": "Partly Cloudy",
|
||||
"2-night": "Partly Cloudy",
|
||||
"3-day": "Cloudy",
|
||||
"3-night": "Cloudy",
|
||||
"45-day": "Foggy",
|
||||
"45-night": "Foggy",
|
||||
"48-day": "Foggy",
|
||||
"48-night": "Foggy",
|
||||
"51-day": "Light Drizzle",
|
||||
"51-night": "Light Drizzle",
|
||||
"53-day": "Drizzle",
|
||||
"53-night": "Drizzle",
|
||||
"55-day": "Heavy Drizzle",
|
||||
"55-night": "Heavy Drizzle",
|
||||
"56-day": "Light Freezing Drizzle",
|
||||
"56-night": "Light Freezing Drizzle",
|
||||
"65-night": "Heavy Rain",
|
||||
"57-day": "Freezing Drizzle",
|
||||
"57-night": "Freezing Drizzle",
|
||||
"61-day": "Light Rain",
|
||||
"61-night": "Light Rain",
|
||||
"63-day": "Rain",
|
||||
"63-night": "Rain",
|
||||
"65-day": "Heavy Rain",
|
||||
"66-night": "Freezing Rain",
|
||||
"67-day": "Freezing Rain",
|
||||
"67-night": "Freezing Rain",
|
||||
"71-day": "Light Snow",
|
||||
"71-night": "Light Snow",
|
||||
"73-day": "Snow",
|
||||
"73-night": "Snow",
|
||||
"75-day": "Heavy Snow",
|
||||
"75-night": "Heavy Snow",
|
||||
"77-day": "Snow Grains",
|
||||
"77-night": "Snow Grains",
|
||||
"80-day": "Light Showers",
|
||||
"80-night": "Light Showers",
|
||||
"81-day": "Showers",
|
||||
"81-night": "Showers",
|
||||
"82-day": "Heavy Showers",
|
||||
"82-night": "Heavy Showers",
|
||||
"85-day": "Snow Showers",
|
||||
"85-night": "Snow Showers",
|
||||
"86-day": "Snow Showers",
|
||||
"86-night": "Snow Showers",
|
||||
"96-night": "Thunderstorm With Hail",
|
||||
"99-day": "Thunderstorm With Hail",
|
||||
"99-night": "Thunderstorm With Hail"
|
||||
"66-day": "Pluja gelada",
|
||||
"95-day": "Tempesta",
|
||||
"95-night": "Tempesta",
|
||||
"96-day": "Tempesta amb calamarsa",
|
||||
"0-day": "Assolellat",
|
||||
"0-night": "Cel clar",
|
||||
"1-day": "Majorment assolellat",
|
||||
"1-night": "Majorment clar",
|
||||
"2-day": "Parcialment ennuvolat",
|
||||
"2-night": "Parcialment ennuvolat",
|
||||
"3-day": "Ennuvolat",
|
||||
"3-night": "Ennuvolat",
|
||||
"45-day": "Boirós",
|
||||
"45-night": "Boirós",
|
||||
"48-day": "Boirós",
|
||||
"48-night": "Boirós",
|
||||
"51-day": "Ruixats lleugers",
|
||||
"51-night": "Ruixats lleugers",
|
||||
"53-day": "Ruixat",
|
||||
"53-night": "Ruxiat",
|
||||
"55-day": "Ruixat intens",
|
||||
"55-night": "Ruixat intens",
|
||||
"56-day": "Lleuger ruixat gelat",
|
||||
"56-night": "Lleuger ruixat gelat",
|
||||
"65-night": "Pluja intensa",
|
||||
"57-day": "Ruixat gelat",
|
||||
"57-night": "Ruixat gelat",
|
||||
"61-day": "Pluja lleugera",
|
||||
"61-night": "Pluja lleugera",
|
||||
"63-day": "Pluja",
|
||||
"63-night": "Pluja",
|
||||
"65-day": "Pluja intensa",
|
||||
"66-night": "Pluja gelada",
|
||||
"67-day": "Pluja gelada",
|
||||
"67-night": "Pluja gelada",
|
||||
"71-day": "Neu lleugera",
|
||||
"71-night": "Neu lleugera",
|
||||
"73-day": "Neu",
|
||||
"73-night": "Neu",
|
||||
"75-day": "Neu intensa",
|
||||
"75-night": "Neu intensa",
|
||||
"77-day": "Neu lleugera",
|
||||
"77-night": "Neu lleugera",
|
||||
"80-day": "Plovisqueig",
|
||||
"80-night": "Plovisqueig",
|
||||
"81-day": "Xàfecs",
|
||||
"81-night": "Xàfecs",
|
||||
"82-day": "Xàfecs intensos",
|
||||
"82-night": "Xàfecs intensos",
|
||||
"85-day": "Xàfecs de neu",
|
||||
"85-night": "Xàfecs de neu",
|
||||
"86-day": "Xàfecs de neu",
|
||||
"86-night": "Xàfecs de neu",
|
||||
"96-night": "Tempesta amb calamarsa",
|
||||
"99-day": "Tempesta amb calamarsa",
|
||||
"99-night": "Tempesta amb calamarsa"
|
||||
},
|
||||
"quicklaunch": {
|
||||
"bookmark": "Bookmark",
|
||||
"service": "Service"
|
||||
"bookmark": "Marcador",
|
||||
"service": "Servei"
|
||||
},
|
||||
"homebridge": {
|
||||
"available_update": "System",
|
||||
"updates": "Updates",
|
||||
"update_available": "Update Available",
|
||||
"up_to_date": "Up to Date",
|
||||
"available_update": "Sistema",
|
||||
"updates": "Actualitzacions",
|
||||
"update_available": "Actualització disponible",
|
||||
"up_to_date": "Actualitzat",
|
||||
"child_bridges": "Child Bridges",
|
||||
"child_bridges_status": "{{ok}}/{{total}}"
|
||||
},
|
||||
"autobrr": {
|
||||
"approvedPushes": "Approved",
|
||||
"rejectedPushes": "Rejected",
|
||||
"filters": "Filters",
|
||||
"indexers": "Indexers"
|
||||
"approvedPushes": "Aprovat",
|
||||
"rejectedPushes": "Rebutjat",
|
||||
"filters": "Filtres",
|
||||
"indexers": "Indexadors"
|
||||
},
|
||||
"watchtower": {
|
||||
"containers_scanned": "Scanned",
|
||||
"containers_updated": "Updated",
|
||||
"containers_failed": "Failed"
|
||||
"containers_scanned": "Escanejat",
|
||||
"containers_updated": "Actualitzat",
|
||||
"containers_failed": "Error"
|
||||
},
|
||||
"tubearchivist": {
|
||||
"downloads": "Queue",
|
||||
"videos": "Videos",
|
||||
"channels": "Channels",
|
||||
"playlists": "Playlists"
|
||||
"downloads": "Cua",
|
||||
"videos": "Vídeos",
|
||||
"channels": "Canals",
|
||||
"playlists": "Llistes de reproducció"
|
||||
},
|
||||
"truenas": {
|
||||
"load": "System Load",
|
||||
"uptime": "Uptime",
|
||||
"alerts": "Alerts",
|
||||
"load": "Càrrega del sistema",
|
||||
"uptime": "Temps actiu",
|
||||
"alerts": "Alertes",
|
||||
"time": "{{value, number(style: unit; unitDisplay: long;)}}"
|
||||
},
|
||||
"navidrome": {
|
||||
"nothing_streaming": "No Active Streams",
|
||||
"please_wait": "Please Wait"
|
||||
"nothing_streaming": "Cap reproducció activa",
|
||||
"please_wait": "Espereu si us plau"
|
||||
},
|
||||
"pyload": {
|
||||
"speed": "Speed",
|
||||
"active": "Active",
|
||||
"queue": "Queue",
|
||||
"speed": "Velocitat",
|
||||
"active": "Actiu",
|
||||
"queue": "Cua",
|
||||
"total": "Total"
|
||||
},
|
||||
"gluetun": {
|
||||
"public_ip": "Public IP",
|
||||
"region": "Region",
|
||||
"country": "Country"
|
||||
"public_ip": "IP Pública",
|
||||
"region": "Regió",
|
||||
"country": "País"
|
||||
},
|
||||
"hdhomerun": {
|
||||
"channels": "Channels",
|
||||
"channels": "Canals",
|
||||
"hd": "HD"
|
||||
},
|
||||
"ping": {
|
||||
@@ -336,30 +336,48 @@
|
||||
"ping": "Ping"
|
||||
},
|
||||
"scrutiny": {
|
||||
"passed": "Passed",
|
||||
"failed": "Failed",
|
||||
"unknown": "Unknown"
|
||||
"passed": "Aprobat",
|
||||
"failed": "Error",
|
||||
"unknown": "Desconegut"
|
||||
},
|
||||
"paperlessngx": {
|
||||
"inbox": "Inbox",
|
||||
"inbox": "Safata d'entrada",
|
||||
"total": "Total"
|
||||
},
|
||||
"deluge": {
|
||||
"seed": "Seed",
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"leech": "Leech"
|
||||
"seed": "Llavor",
|
||||
"download": "Descàrrega",
|
||||
"upload": "Pujada",
|
||||
"leech": "Company"
|
||||
},
|
||||
"diskstation": {
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
"download": "Descàrrega",
|
||||
"upload": "Pujada",
|
||||
"leech": "Company",
|
||||
"seed": "Llavor"
|
||||
},
|
||||
"flood": {
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
"download": "Descarregar",
|
||||
"upload": "Pujada",
|
||||
"leech": "Company",
|
||||
"seed": "Llavor"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"url": "URL",
|
||||
"information": "Information",
|
||||
"raw_error": "Raw Error",
|
||||
"response_data": "Response Data"
|
||||
"response_data": "Empfangene Daten"
|
||||
},
|
||||
"search": {
|
||||
"placeholder": "Suche…"
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"unread": "Unread",
|
||||
"read": "Read"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
"bytes": "{{value, bytes}}",
|
||||
"bits": "{{value, bytes(bits: true)}}",
|
||||
"bbytes": "{{value, bytes(binary: true)}}",
|
||||
"bbits": "{{value, bytes(bits: true, binary: true)}}",
|
||||
"byterate": "{{value, rate}}",
|
||||
"bbits": "{{value, bytes(bits: true; binary: true)}}",
|
||||
"byterate": "{{value, rate(bits: false)}}",
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bitrate": "{{value, rate(bits: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}",
|
||||
"percent": "{{value, percent}}",
|
||||
"number": "{{value, number}}",
|
||||
"ms": "{{value, number}}"
|
||||
@@ -192,6 +194,12 @@
|
||||
"stopped": "Stopped",
|
||||
"total": "Total"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"traefik": {
|
||||
"routers": "Routers",
|
||||
"services": "Services",
|
||||
@@ -240,6 +248,10 @@
|
||||
"status_count": "Posts",
|
||||
"domain_count": "Domains"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"authentik": {
|
||||
"users": "Users",
|
||||
"loginsLast24H": "Logins (24h)",
|
||||
@@ -372,5 +384,9 @@
|
||||
"paperlessngx": {
|
||||
"inbox": "Inbox",
|
||||
"total": "Total"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Subir",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"saved": "Saved",
|
||||
"errored": "Errored"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Envoi",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "À traiter",
|
||||
"processed": "Traité",
|
||||
"errored": "En erreur",
|
||||
"saved": "Enregistré"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}",
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"available": "Dostupno",
|
||||
"pending": "Predstoji",
|
||||
"approved": "Odobreno",
|
||||
"processing": "Processing"
|
||||
"processing": "Obrada"
|
||||
},
|
||||
"pihole": {
|
||||
"queries": "Upiti",
|
||||
@@ -345,21 +345,39 @@
|
||||
"total": "Ukupno"
|
||||
},
|
||||
"deluge": {
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"download": "Preuzimanje",
|
||||
"upload": "Prijenos",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"diskstation": {
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"download": "Preuzimanje",
|
||||
"upload": "Prijenos",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"flood": {
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"download": "Preuzimanje",
|
||||
"upload": "Prijenos",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"unread": "Unread",
|
||||
"read": "Read"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Wysyłanie",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
"widget": {
|
||||
"missing_type": "Widget ausente: {{type}}",
|
||||
"api_error": "Erro da API",
|
||||
"status": "Status",
|
||||
"information": "Information",
|
||||
"url": "URL",
|
||||
"raw_error": "Raw Error",
|
||||
"response_data": "Response Data"
|
||||
"status": "Estado",
|
||||
"information": "Informação",
|
||||
"url": "Endereço URL",
|
||||
"raw_error": "Erro",
|
||||
"response_data": "Dados da Resposta"
|
||||
},
|
||||
"search": {
|
||||
"placeholder": "Pesquisar…"
|
||||
@@ -24,8 +24,8 @@
|
||||
"mem": "Mem",
|
||||
"cpu": "CPU",
|
||||
"offline": "Desligado",
|
||||
"error": "Error",
|
||||
"unknown": "Unknown"
|
||||
"error": "Erro",
|
||||
"unknown": "Desconhecido"
|
||||
},
|
||||
"emby": {
|
||||
"playing": "A reproduzir",
|
||||
@@ -104,7 +104,9 @@
|
||||
"byterate": "{{value, bytes}}",
|
||||
"ms": "{{value, number}}",
|
||||
"bitrate": "{{value, bytes(bits: true)}}",
|
||||
"percent": "{{value, percent}}"
|
||||
"percent": "{{value, percent}}",
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
},
|
||||
"weather": {
|
||||
"current": "Localização atual",
|
||||
@@ -150,7 +152,7 @@
|
||||
"transmission": {
|
||||
"download": "Baixando",
|
||||
"upload": "Enviando",
|
||||
"leech": "Sanguessugas",
|
||||
"leech": "Leech",
|
||||
"seed": "Semeadores"
|
||||
},
|
||||
"jackett": {
|
||||
@@ -201,55 +203,55 @@
|
||||
"vms": "VMs"
|
||||
},
|
||||
"unifi": {
|
||||
"users": "Users",
|
||||
"uptime": "System Uptime",
|
||||
"days": "Days",
|
||||
"users": "Utilizadores",
|
||||
"uptime": "Tempo de Atividade do Sistema",
|
||||
"days": "Dias",
|
||||
"wan": "WAN",
|
||||
"lan_users": "LAN Users",
|
||||
"wlan_users": "WLAN Users",
|
||||
"up": "UP",
|
||||
"down": "DOWN",
|
||||
"wait": "Please wait",
|
||||
"lan_users": "Utilizadores LAN",
|
||||
"wlan_users": "Utilizadores WLAN",
|
||||
"up": "Ligados",
|
||||
"down": "Desligados",
|
||||
"wait": "Por favor, aguarde",
|
||||
"lan": "LAN",
|
||||
"wlan": "WLAN",
|
||||
"devices": "Devices",
|
||||
"lan_devices": "LAN Devices",
|
||||
"wlan_devices": "WLAN Devices"
|
||||
"devices": "Dispositivos",
|
||||
"lan_devices": "Dispositivos LAN",
|
||||
"wlan_devices": "Dispositivos WLAN"
|
||||
},
|
||||
"plex": {
|
||||
"streams": "Active Streams",
|
||||
"movies": "Movies",
|
||||
"tv": "TV Shows"
|
||||
"streams": "Streams Ativas",
|
||||
"movies": "Filmes",
|
||||
"tv": "Series de TV"
|
||||
},
|
||||
"glances": {
|
||||
"cpu": "CPU",
|
||||
"mem": "MEM",
|
||||
"wait": "Please wait"
|
||||
"wait": "Por favor, aguarde"
|
||||
},
|
||||
"changedetectionio": {
|
||||
"totalObserved": "Total Observed",
|
||||
"diffsDetected": "Diffs Detected"
|
||||
"totalObserved": "Total Observado",
|
||||
"diffsDetected": "Diferenças Detetadas"
|
||||
},
|
||||
"wmo": {
|
||||
"0-day": "Sunny",
|
||||
"0-night": "Clear",
|
||||
"1-day": "Mainly Sunny",
|
||||
"1-night": "Mainly Clear",
|
||||
"2-day": "Partly Cloudy",
|
||||
"2-night": "Partly Cloudy",
|
||||
"3-day": "Cloudy",
|
||||
"3-night": "Cloudy",
|
||||
"0-day": "Solarengo",
|
||||
"0-night": "Limpo",
|
||||
"1-day": "Maioritariamente ensolarado",
|
||||
"1-night": "Maioritariamente Limpo",
|
||||
"2-day": "Parcialmente Nublado",
|
||||
"2-night": "Parcialmente nublado",
|
||||
"3-day": "Nublado",
|
||||
"3-night": "Nublado",
|
||||
"99-night": "Thunderstorm With Hail",
|
||||
"45-day": "Foggy",
|
||||
"45-night": "Foggy",
|
||||
"48-day": "Foggy",
|
||||
"48-night": "Foggy",
|
||||
"51-day": "Light Drizzle",
|
||||
"51-night": "Light Drizzle",
|
||||
"53-day": "Drizzle",
|
||||
"53-night": "Drizzle",
|
||||
"55-day": "Heavy Drizzle",
|
||||
"55-night": "Heavy Drizzle",
|
||||
"45-day": "Nevoeiro",
|
||||
"45-night": "Nevoeiro",
|
||||
"48-day": "Nevoeiro",
|
||||
"48-night": "Nevoeiro",
|
||||
"51-day": "Aguaceiros",
|
||||
"51-night": "Aguaceiros",
|
||||
"53-day": "Chuvisco",
|
||||
"53-night": "Chuvisco",
|
||||
"55-day": "Aguaceiro Forte",
|
||||
"55-night": "Aguaceiro Forte",
|
||||
"56-day": "Light Freezing Drizzle",
|
||||
"56-night": "Light Freezing Drizzle",
|
||||
"57-day": "Freezing Drizzle",
|
||||
@@ -289,8 +291,8 @@
|
||||
"99-day": "Thunderstorm With Hail"
|
||||
},
|
||||
"quicklaunch": {
|
||||
"bookmark": "Bookmark",
|
||||
"service": "Service"
|
||||
"bookmark": "Marcador",
|
||||
"service": "Serviço"
|
||||
},
|
||||
"homebridge": {
|
||||
"available_update": "System",
|
||||
@@ -343,7 +345,7 @@
|
||||
"hd": "HD"
|
||||
},
|
||||
"ping": {
|
||||
"error": "Error",
|
||||
"error": "Erro",
|
||||
"ping": "Ping"
|
||||
},
|
||||
"scrutiny": {
|
||||
@@ -368,9 +370,23 @@
|
||||
"seed": "Seed"
|
||||
},
|
||||
"flood": {
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"download": "Descarregar",
|
||||
"upload": "Carregar",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"download": "Download",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"seed": "Seed",
|
||||
"upload": "Upload",
|
||||
"leech": "Leech"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"seed": "Seed",
|
||||
"leech": "Leech"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"download": "Download",
|
||||
"upload": "Upload",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"saved": "Saved",
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,23 @@
|
||||
"upload": "Upload",
|
||||
"leech": "Leech",
|
||||
"seed": "Seed"
|
||||
},
|
||||
"tdarr": {
|
||||
"queue": "Queue",
|
||||
"processed": "Processed",
|
||||
"errored": "Errored",
|
||||
"saved": "Saved"
|
||||
},
|
||||
"miniflux": {
|
||||
"read": "Read",
|
||||
"unread": "Unread"
|
||||
},
|
||||
"nextdns": {
|
||||
"wait": "Please Wait",
|
||||
"no_devices": "No Device Data Received"
|
||||
},
|
||||
"common": {
|
||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import Image from "next/future/image";
|
||||
|
||||
export default function ResolvedIcon({ icon }) {
|
||||
export default function ResolvedIcon({ icon, width = 32, height = 32 }) {
|
||||
// direct or relative URLs
|
||||
if (icon.startsWith("http") || icon.startsWith("/")) {
|
||||
return <Image src={`${icon}`} width={32} height={32} alt="logo" />;
|
||||
return <Image src={`${icon}`} width={width} height={height} alt="logo" />;
|
||||
}
|
||||
|
||||
// mdi- prefixed, material design icons
|
||||
@@ -12,8 +12,8 @@ export default function ResolvedIcon({ icon }) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
width,
|
||||
height,
|
||||
maxWidth: '100%',
|
||||
maxHeight: '100%',
|
||||
background: "linear-gradient(180deg, rgb(var(--color-logo-start)), rgb(var(--color-logo-stop)))",
|
||||
@@ -29,8 +29,8 @@ export default function ResolvedIcon({ icon }) {
|
||||
return (
|
||||
<Image
|
||||
src={`https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${iconName}.png`}
|
||||
width={32}
|
||||
height={32}
|
||||
width={width}
|
||||
height={height}
|
||||
alt="logo"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,56 +1,62 @@
|
||||
export default function Logo() {
|
||||
import ResolvedIcon from "components/resolvedicon"
|
||||
|
||||
export default function Logo({ options }) {
|
||||
return (
|
||||
<div className="w-12 h-12 flex flex-row items-center align-middle mr-3 self-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 1024 1024"
|
||||
style={{
|
||||
enableBackground: "new 0 0 1024 1024",
|
||||
}}
|
||||
xmlSpace="preserve"
|
||||
className="w-full h-full"
|
||||
>
|
||||
<style>
|
||||
{
|
||||
".st0{display:none}.st3{stroke-linecap:square}.st3,.st4{fill:none;stroke:#fff;stroke-miterlimit:10}.st6{display:inline;fill:#333}.st7{fill:#fff}"
|
||||
}
|
||||
</style>
|
||||
<g id="Icon">
|
||||
<path
|
||||
d="M771.9 191c27.7 0 50.1 26.5 50.1 59.3v186.4l-100.2.3V250.3c0-32.8 22.4-59.3 50.1-59.3z"
|
||||
style={{
|
||||
fill: "rgba(var(--color-logo-start))",
|
||||
}}
|
||||
/>
|
||||
<linearGradient
|
||||
id="homepage_logo_gradient"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1={200.746}
|
||||
y1={225.015}
|
||||
x2={764.986}
|
||||
y2={789.255}
|
||||
>
|
||||
<stop
|
||||
offset={0}
|
||||
{options.icon ?
|
||||
<ResolvedIcon icon={options.icon} width={48} height={48} /> :
|
||||
// fallback to homepage logo
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 1024 1024"
|
||||
style={{
|
||||
enableBackground: "new 0 0 1024 1024",
|
||||
}}
|
||||
xmlSpace="preserve"
|
||||
className="w-full h-full"
|
||||
>
|
||||
<style>
|
||||
{
|
||||
".st0{display:none}.st3{stroke-linecap:square}.st3,.st4{fill:none;stroke:#fff;stroke-miterlimit:10}.st6{display:inline;fill:#333}.st7{fill:#fff}"
|
||||
}
|
||||
</style>
|
||||
<g id="Icon">
|
||||
<path
|
||||
d="M771.9 191c27.7 0 50.1 26.5 50.1 59.3v186.4l-100.2.3V250.3c0-32.8 22.4-59.3 50.1-59.3z"
|
||||
style={{
|
||||
stopColor: "rgba(var(--color-logo-start))",
|
||||
fill: "rgba(var(--color-logo-start))",
|
||||
}}
|
||||
/>
|
||||
<stop
|
||||
offset={1}
|
||||
<linearGradient
|
||||
id="homepage_logo_gradient"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1={200.746}
|
||||
y1={225.015}
|
||||
x2={764.986}
|
||||
y2={789.255}
|
||||
>
|
||||
<stop
|
||||
offset={0}
|
||||
style={{
|
||||
stopColor: "rgba(var(--color-logo-start))",
|
||||
}}
|
||||
/>
|
||||
<stop
|
||||
offset={1}
|
||||
style={{
|
||||
stopColor: "rgba(var(--color-logo-stop))",
|
||||
}}
|
||||
/>
|
||||
</linearGradient>
|
||||
<path
|
||||
d="M721.8 250.3c0-32.7 22.4-59.3 50.1-59.3H253.1c-27.7 0-50.1 26.5-50.1 59.3v582.2l90.2-75.7-.1-130.3H375v61.8l88-73.8 258.8 217.9V250.6"
|
||||
style={{
|
||||
stopColor: "rgba(var(--color-logo-stop))",
|
||||
fill: "url(#homepage_logo_gradient)",
|
||||
}}
|
||||
/>
|
||||
</linearGradient>
|
||||
<path
|
||||
d="M721.8 250.3c0-32.7 22.4-59.3 50.1-59.3H253.1c-27.7 0-50.1 26.5-50.1 59.3v582.2l90.2-75.7-.1-130.3H375v61.8l88-73.8 258.8 217.9V250.6"
|
||||
style={{
|
||||
fill: "url(#homepage_logo_gradient)",
|
||||
}}
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</g>
|
||||
</svg>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ export default async function handler(req, res) {
|
||||
}
|
||||
|
||||
try {
|
||||
const docker = new Docker(getDockerArguments(containerServer));
|
||||
const dockerArgs = getDockerArguments(containerServer);
|
||||
const docker = new Docker(dockerArgs.conn);
|
||||
const containers = await docker.listContainers({
|
||||
all: true,
|
||||
});
|
||||
@@ -31,18 +32,44 @@ export default async function handler(req, res) {
|
||||
const containerNames = containers.map((container) => container.Names[0].replace(/^\//, ""));
|
||||
const containerExists = containerNames.includes(containerName);
|
||||
|
||||
if (!containerExists) {
|
||||
res.status(200).send({
|
||||
error: "not found",
|
||||
if (containerExists) {
|
||||
const container = docker.getContainer(containerName);
|
||||
const stats = await container.stats({ stream: false });
|
||||
|
||||
res.status(200).json({
|
||||
stats,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const container = docker.getContainer(containerName);
|
||||
const stats = await container.stats({ stream: false });
|
||||
// Try with a service deployed in Docker Swarm, if enabled
|
||||
if (dockerArgs.swarm) {
|
||||
const tasks = await docker.listTasks({
|
||||
filters: {
|
||||
service: [containerName],
|
||||
// A service can have several offline containers, so we only look for an active one.
|
||||
"desired-state": ["running"],
|
||||
},
|
||||
})
|
||||
.catch(() => []);
|
||||
|
||||
res.status(200).json({
|
||||
stats,
|
||||
// For now we are only interested in the first one (in case replicas > 1).
|
||||
// TODO: Show the result for all replicas/containers?
|
||||
const taskContainerId = tasks.at(0)?.Status?.ContainerStatus?.ContainerID;
|
||||
|
||||
if (taskContainerId) {
|
||||
const container = docker.getContainer(taskContainerId);
|
||||
const stats = await container.stats({ stream: false });
|
||||
|
||||
res.status(200).json({
|
||||
stats,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
res.status(200).send({
|
||||
error: "not found",
|
||||
});
|
||||
} catch {
|
||||
res.status(500).send({
|
||||
|
||||
@@ -13,7 +13,8 @@ export default async function handler(req, res) {
|
||||
}
|
||||
|
||||
try {
|
||||
const docker = new Docker(getDockerArguments(containerServer));
|
||||
const dockerArgs = getDockerArguments(containerServer);
|
||||
const docker = new Docker(dockerArgs.conn);
|
||||
const containers = await docker.listContainers({
|
||||
all: true,
|
||||
});
|
||||
@@ -29,18 +30,43 @@ export default async function handler(req, res) {
|
||||
const containerNames = containers.map((container) => container.Names[0].replace(/^\//, ""));
|
||||
const containerExists = containerNames.includes(containerName);
|
||||
|
||||
if (!containerExists) {
|
||||
return res.status(200).send({
|
||||
error: "not found",
|
||||
if (containerExists) {
|
||||
const container = docker.getContainer(containerName);
|
||||
const info = await container.inspect();
|
||||
|
||||
return res.status(200).json({
|
||||
status: info.State.Status,
|
||||
health: info.State.Health?.Status,
|
||||
});
|
||||
}
|
||||
|
||||
const container = docker.getContainer(containerName);
|
||||
const info = await container.inspect();
|
||||
if (dockerArgs.swarm) {
|
||||
const tasks = await docker.listTasks({
|
||||
filters: {
|
||||
service: [containerName],
|
||||
// A service can have several offline containers, we only look for an active one.
|
||||
"desired-state": ["running"],
|
||||
},
|
||||
})
|
||||
.catch(() => []);
|
||||
|
||||
return res.status(200).json({
|
||||
status: info.State.Status,
|
||||
health: info.State.Health?.Status
|
||||
// For now we are only interested in the first one (in case replicas > 1).
|
||||
// TODO: Show the result for all replicas/containers?
|
||||
const taskContainerId = tasks.at(0)?.Status?.ContainerStatus?.ContainerID;
|
||||
|
||||
if (taskContainerId) {
|
||||
const container = docker.getContainer(taskContainerId);
|
||||
const info = await container.inspect();
|
||||
|
||||
return res.status(200).json({
|
||||
status: info.State.Status,
|
||||
health: info.State.Health?.Status,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
error: "not found",
|
||||
});
|
||||
} catch {
|
||||
return res.status(500).send({
|
||||
|
||||
@@ -22,11 +22,14 @@ export default function getDockerArguments(server) {
|
||||
|
||||
if (servers[server]) {
|
||||
if (servers[server].socket) {
|
||||
return { socketPath: servers[server].socket };
|
||||
return { conn: { socketPath: servers[server].socket }, swarm: !!servers[server].swarm };
|
||||
}
|
||||
|
||||
if (servers[server].host) {
|
||||
return { host: servers[server].host, port: servers[server].port || null };
|
||||
return {
|
||||
conn: { host: servers[server].host, port: servers[server].port || null },
|
||||
swarm: !!servers[server].swarm,
|
||||
};
|
||||
}
|
||||
|
||||
return servers[server];
|
||||
|
||||
@@ -44,7 +44,7 @@ export async function servicesFromDocker() {
|
||||
|
||||
const serviceServers = await Promise.all(
|
||||
Object.keys(servers).map(async (serverName) => {
|
||||
const docker = new Docker(getDockerArguments(serverName));
|
||||
const docker = new Docker(getDockerArguments(serverName).conn);
|
||||
const containers = await docker.listContainers({
|
||||
all: true,
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@ import widgets from "widgets/widgets";
|
||||
|
||||
const logger = createLogger("credentialedProxyHandler");
|
||||
|
||||
export default async function credentialedProxyHandler(req, res) {
|
||||
export default async function credentialedProxyHandler(req, res, map) {
|
||||
const { group, service, endpoint } = req.query;
|
||||
|
||||
if (group && service) {
|
||||
@@ -36,6 +36,8 @@ export default async function credentialedProxyHandler(req, res) {
|
||||
headers["X-API-Token"] = `${widget.key}`;
|
||||
} else if (widget.type === "tubearchivist") {
|
||||
headers.Authorization = `Token ${widget.key}`;
|
||||
} else if (widget.type === "miniflux") {
|
||||
headers["X-Auth-Token"] = `${widget.key}`;
|
||||
} else {
|
||||
headers["X-API-Key"] = `${widget.key}`;
|
||||
}
|
||||
@@ -47,6 +49,8 @@ export default async function credentialedProxyHandler(req, res) {
|
||||
headers,
|
||||
});
|
||||
|
||||
let resultData = data;
|
||||
|
||||
if (status === 204 || status === 304) {
|
||||
return res.status(status).end();
|
||||
}
|
||||
@@ -59,8 +63,12 @@ export default async function credentialedProxyHandler(req, res) {
|
||||
return res.status(500).json({error: {message: "Invalid data", url, data}});
|
||||
}
|
||||
|
||||
if (status === 200 && map) {
|
||||
resultData = map(data);
|
||||
}
|
||||
|
||||
if (contentType) res.setHeader("Content-Type", contentType);
|
||||
return res.status(status).send(data);
|
||||
return res.status(status).send(resultData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,9 @@ const components = {
|
||||
jellyseerr: dynamic(() => import("./jellyseerr/component")),
|
||||
lidarr: dynamic(() => import("./lidarr/component")),
|
||||
mastodon: dynamic(() => import("./mastodon/component")),
|
||||
miniflux: dynamic(() => import("./miniflux/component")),
|
||||
navidrome: dynamic(() => import("./navidrome/component")),
|
||||
nextdns: dynamic(() => import("./nextdns/component")),
|
||||
npm: dynamic(() => import("./npm/component")),
|
||||
nzbget: dynamic(() => import("./nzbget/component")),
|
||||
ombi: dynamic(() => import("./ombi/component")),
|
||||
@@ -43,6 +45,7 @@ const components = {
|
||||
speedtest: dynamic(() => import("./speedtest/component")),
|
||||
strelaysrv: dynamic(() => import("./strelaysrv/component")),
|
||||
tautulli: dynamic(() => import("./tautulli/component")),
|
||||
tdarr: dynamic(() => import("./tdarr/component")),
|
||||
traefik: dynamic(() => import("./traefik/component")),
|
||||
transmission: dynamic(() => import("./transmission/component")),
|
||||
tubearchivist: dynamic(() => import("./tubearchivist/component")),
|
||||
|
||||
@@ -11,11 +11,11 @@ export default function Component({ service }) {
|
||||
|
||||
const { data: torrentData, error: torrentError } = useWidgetAPI(widget, "torrents");
|
||||
|
||||
if (torrentError) {
|
||||
return <Container error={torrentError} />;
|
||||
if (torrentError || !torrentData?.torrents) {
|
||||
return <Container error={torrentError ?? {message: "No torrent data returned"}} />;
|
||||
}
|
||||
|
||||
if (!torrentData) {
|
||||
if (!torrentData || !torrentData.torrents) {
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block label="flood.leech" />
|
||||
|
||||
@@ -10,7 +10,7 @@ const proxyName = "homebridgeProxyHandler";
|
||||
const sessionTokenCacheKey = `${proxyName}__sessionToken`;
|
||||
const logger = createLogger(proxyName);
|
||||
|
||||
async function login(widget) {
|
||||
async function login(widget, service) {
|
||||
const endpoint = "auth/login";
|
||||
const api = widgets?.[widget.type]?.api
|
||||
const loginUrl = new URL(formatApiCall(api, { endpoint, ...widget }));
|
||||
@@ -26,7 +26,7 @@ async function login(widget) {
|
||||
try {
|
||||
const { access_token: accessToken, expires_in: expiresIn } = JSON.parse(data.toString());
|
||||
|
||||
cache.put(sessionTokenCacheKey, accessToken, (expiresIn * 1000) - 5 * 60 * 1000); // expiresIn (s) - 5m
|
||||
cache.put(`${sessionTokenCacheKey}.${service}`, accessToken, (expiresIn * 1000) - 5 * 60 * 1000); // expiresIn (s) - 5m
|
||||
return { accessToken };
|
||||
} catch (e) {
|
||||
logger.error("Unable to login to Homebridge API: %s", e);
|
||||
@@ -35,10 +35,11 @@ async function login(widget) {
|
||||
return { accessToken: false };
|
||||
}
|
||||
|
||||
async function apiCall(widget, endpoint) {
|
||||
async function apiCall(widget, endpoint, service) {
|
||||
const key = `${sessionTokenCacheKey}.${service}`;
|
||||
const headers = {
|
||||
"content-type": "application/json",
|
||||
"Authorization": `Bearer ${cache.get(sessionTokenCacheKey)}`,
|
||||
"Authorization": `Bearer ${cache.get(key)}`,
|
||||
}
|
||||
|
||||
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
|
||||
@@ -51,7 +52,7 @@ async function apiCall(widget, endpoint) {
|
||||
|
||||
if (status === 401) {
|
||||
logger.debug("Homebridge API rejected the request, attempting to obtain new session token");
|
||||
const { accessToken } = login(widget);
|
||||
const { accessToken } = login(widget, service);
|
||||
headers.Authorization = `Bearer ${accessToken}`;
|
||||
|
||||
// retry the request, now with the new session token
|
||||
@@ -83,14 +84,14 @@ export default async function homebridgeProxyHandler(req, res) {
|
||||
return res.status(400).json({ error: "Invalid proxy service type" });
|
||||
}
|
||||
|
||||
if (!cache.get(sessionTokenCacheKey)) {
|
||||
await login(widget);
|
||||
if (!cache.get(`${sessionTokenCacheKey}.${service}`)) {
|
||||
await login(widget, service);
|
||||
}
|
||||
|
||||
const { data: statusData } = await apiCall(widget, "status/homebridge");
|
||||
const { data: versionData } = await apiCall(widget, "status/homebridge-version");
|
||||
const { data: childBridgeData } = await apiCall(widget, "status/homebridge/child-bridges");
|
||||
const { data: pluginsData } = await apiCall(widget, "plugins");
|
||||
const { data: statusData } = await apiCall(widget, "status/homebridge", service);
|
||||
const { data: versionData } = await apiCall(widget, "status/homebridge-version", service);
|
||||
const { data: childBridgeData } = await apiCall(widget, "status/homebridge/child-bridges", service);
|
||||
const { data: pluginsData } = await apiCall(widget, "plugins", service);
|
||||
|
||||
return res.status(200).send({
|
||||
status: statusData?.status,
|
||||
|
||||
33
src/widgets/miniflux/component.jsx
Normal file
33
src/widgets/miniflux/component.jsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { useTranslation } from "next-i18next";
|
||||
|
||||
import Container from "components/services/widget/container";
|
||||
import Block from "components/services/widget/block";
|
||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||
|
||||
export default function Component({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { widget } = service;
|
||||
|
||||
const { data: minifluxData, error: minifluxError } = useWidgetAPI(widget, "counters");
|
||||
|
||||
if (minifluxError) {
|
||||
return <Container error={minifluxError} />;
|
||||
}
|
||||
|
||||
if (!minifluxData) {
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block label="miniflux.unread" />
|
||||
<Block label="miniflux.read" />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block label="miniflux.unread" value={t("common.number", { value: minifluxData.unread })} />
|
||||
<Block label="miniflux.read" value={t("common.number", { value: minifluxData.read })} />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
19
src/widgets/miniflux/widget.js
Normal file
19
src/widgets/miniflux/widget.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { asJson } from "utils/proxy/api-helpers";
|
||||
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
|
||||
|
||||
const widget = {
|
||||
api: "{url}/v1/{endpoint}",
|
||||
proxyHandler: credentialedProxyHandler,
|
||||
|
||||
mappings: {
|
||||
counters: {
|
||||
endpoint: "feeds/counters",
|
||||
map: (data) => ({
|
||||
read: Object.values(asJson(data).reads).reduce((acc, i) => acc + i, 0),
|
||||
unread: Object.values(asJson(data).unreads).reduce((acc, i) => acc + i, 0)
|
||||
}),
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
export default widget;
|
||||
39
src/widgets/nextdns/component.jsx
Normal file
39
src/widgets/nextdns/component.jsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { useTranslation } from "next-i18next";
|
||||
|
||||
import Container from "components/services/widget/container";
|
||||
import Block from "components/services/widget/block";
|
||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||
|
||||
export default function Component({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { widget } = service;
|
||||
|
||||
const { data: nextdnsData, error: nextdnsError } = useWidgetAPI(widget, "analytics/status");
|
||||
|
||||
if (nextdnsError) {
|
||||
return <Container error={nextdnsError} />;
|
||||
}
|
||||
|
||||
if (!nextdnsData) {
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block key="status" label="widget.status" value={t("nextdns.wait")} />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
if (!nextdnsData?.data?.length) {
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block key="status" label="widget.status" value={t("nextdns.no_devices")} />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Container service={service}>
|
||||
{nextdnsData.data.map(d => <Block key={d.status} label={d.status} value={t("common.number", { value: d.queries })} />)}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
17
src/widgets/nextdns/widget.js
Normal file
17
src/widgets/nextdns/widget.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
|
||||
|
||||
const widget = {
|
||||
api: "https://api.nextdns.io/profiles/{profile}/{endpoint}",
|
||||
proxyHandler: credentialedProxyHandler,
|
||||
|
||||
mappings: {
|
||||
"analytics/status": {
|
||||
endpoint: "analytics/status",
|
||||
validate: [
|
||||
"data",
|
||||
]
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default widget;
|
||||
@@ -10,7 +10,7 @@ const proxyName = "npmProxyHandler";
|
||||
const tokenCacheKey = `${proxyName}__token`;
|
||||
const logger = createLogger(proxyName);
|
||||
|
||||
async function login(loginUrl, username, password) {
|
||||
async function login(loginUrl, username, password, service) {
|
||||
const authResponse = await httpProxy(loginUrl, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ identity: username, secret: password }),
|
||||
@@ -27,7 +27,7 @@ async function login(loginUrl, username, password) {
|
||||
|
||||
if (status === 200) {
|
||||
const expiration = new Date(data.expires) - Date.now();
|
||||
cache.put(tokenCacheKey, data.token, expiration - (5 * 60 * 1000)); // expiration -5 minutes
|
||||
cache.put(`${tokenCacheKey}.${service}`, data.token, expiration - (5 * 60 * 1000)); // expiration -5 minutes
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(`Error ${status} logging into npm`, authResponse[2]);
|
||||
@@ -53,9 +53,9 @@ export default async function npmProxyHandler(req, res) {
|
||||
let contentType;
|
||||
let data;
|
||||
|
||||
let token = cache.get(tokenCacheKey);
|
||||
let token = cache.get(`${tokenCacheKey}.${service}`);
|
||||
if (!token) {
|
||||
[status, token] = await login(loginUrl, widget.username, widget.password);
|
||||
[status, token] = await login(loginUrl, widget.username, widget.password, service);
|
||||
if (status !== 200) {
|
||||
logger.debug(`HTTTP ${status} logging into npm api: ${token}`);
|
||||
return res.status(status).send(token);
|
||||
@@ -72,8 +72,8 @@ export default async function npmProxyHandler(req, res) {
|
||||
|
||||
if (status === 403) {
|
||||
logger.debug(`HTTTP ${status} retrieving data from npm api, logging in and trying again.`);
|
||||
cache.del(tokenCacheKey);
|
||||
[status, token] = await login(loginUrl, widget.username, widget.password);
|
||||
cache.del(`${tokenCacheKey}.${service}`);
|
||||
[status, token] = await login(loginUrl, widget.username, widget.password, service);
|
||||
|
||||
if (status !== 200) {
|
||||
logger.debug(`HTTTP ${status} logging into npm api: ${data}`);
|
||||
|
||||
@@ -58,6 +58,9 @@ async function fetchFromPlexAPI(endpoint, widget) {
|
||||
|
||||
export default async function plexProxyHandler(req, res) {
|
||||
const widget = await getWidget(req);
|
||||
|
||||
const { service } = req.query;
|
||||
|
||||
if (!widget) {
|
||||
return res.status(400).json({ error: "Invalid proxy service type" });
|
||||
}
|
||||
@@ -74,23 +77,24 @@ export default async function plexProxyHandler(req, res) {
|
||||
streams = apiData.MediaContainer._attributes.size;
|
||||
}
|
||||
|
||||
let libraries = cache.get(librariesCacheKey);
|
||||
let libraries = cache.get(`${librariesCacheKey}.${service}`);
|
||||
if (libraries === null) {
|
||||
logger.debug("Getting libraries from Plex API");
|
||||
[status, apiData] = await fetchFromPlexAPI("/library/sections", widget);
|
||||
if (apiData && apiData.MediaContainer) {
|
||||
libraries = apiData.MediaContainer.Directory;
|
||||
cache.put(librariesCacheKey, libraries, 1000 * 60 * 60 * 6);
|
||||
libraries = [].concat(apiData.MediaContainer.Directory);
|
||||
cache.put(`${librariesCacheKey}.${service}`, libraries, 1000 * 60 * 60 * 6);
|
||||
}
|
||||
}
|
||||
|
||||
let movies = cache.get(moviesCacheKey);
|
||||
let tv = cache.get(tvCacheKey);
|
||||
let movies = cache.get(`${moviesCacheKey}.${service}`);
|
||||
let tv = cache.get(`${tvCacheKey}.${service}`);
|
||||
if (movies === null || tv === null) {
|
||||
movies = 0;
|
||||
tv = 0;
|
||||
logger.debug("Getting movie + tv counts from Plex API");
|
||||
libraries.filter(l => ["movie", "show"].includes(l._attributes.type)).forEach(async (library) => {
|
||||
const movieTVLibraries = libraries.filter(l => ["movie", "show"].includes(l._attributes.type));
|
||||
await Promise.all(movieTVLibraries.map(async (library) => {
|
||||
[status, apiData] = await fetchFromPlexAPI(`/library/sections/${library._attributes.key}/all`, widget);
|
||||
if (apiData && apiData.MediaContainer) {
|
||||
const size = parseInt(apiData.MediaContainer._attributes.size, 10);
|
||||
@@ -100,9 +104,9 @@ export default async function plexProxyHandler(req, res) {
|
||||
tv += size;
|
||||
}
|
||||
}
|
||||
cache.put(tvCacheKey, tv, 1000 * 60 * 10);
|
||||
cache.put(moviesCacheKey, movies, 1000 * 60 * 10);
|
||||
});
|
||||
}));
|
||||
cache.put(`${tvCacheKey}.${service}`, tv, 1000 * 60 * 10);
|
||||
cache.put(`${moviesCacheKey}.${service}`, movies, 1000 * 60 * 10);
|
||||
}
|
||||
|
||||
const data = {
|
||||
|
||||
@@ -11,7 +11,7 @@ const logger = createLogger(proxyName);
|
||||
const sessionCacheKey = `${proxyName}__sessionId`;
|
||||
const isNgCacheKey = `${proxyName}__isNg`;
|
||||
|
||||
async function fetchFromPyloadAPI(url, sessionId, params) {
|
||||
async function fetchFromPyloadAPI(url, sessionId, params, service) {
|
||||
const options = {
|
||||
body: params
|
||||
? Object.keys(params)
|
||||
@@ -25,10 +25,10 @@ async function fetchFromPyloadAPI(url, sessionId, params) {
|
||||
};
|
||||
|
||||
// see https://github.com/benphelps/homepage/issues/517
|
||||
const isNg = cache.get(isNgCacheKey);
|
||||
const isNg = cache.get(`${isNgCacheKey}.${service}`);
|
||||
if (isNg && !params) {
|
||||
delete options.body;
|
||||
options.headers.Cookie = cache.get(sessionCacheKey);
|
||||
options.headers.Cookie = cache.get(`${sessionCacheKey}.${service}`);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
@@ -43,19 +43,19 @@ async function fetchFromPyloadAPI(url, sessionId, params) {
|
||||
return [status, returnData, responseHeaders];
|
||||
}
|
||||
|
||||
async function login(loginUrl, username, password = '') {
|
||||
const [status, sessionId, responseHeaders] = await fetchFromPyloadAPI(loginUrl, null, { username, password });
|
||||
async function login(loginUrl, service, username, password = '') {
|
||||
const [status, sessionId, responseHeaders] = await fetchFromPyloadAPI(loginUrl, null, { username, password }, service);
|
||||
|
||||
// this API actually returns status 200 even on login failure
|
||||
if (status !== 200 || sessionId === false) {
|
||||
logger.error(`HTTP ${status} logging into Pyload API, returned: ${JSON.stringify(sessionId)}`);
|
||||
} else if (responseHeaders['set-cookie']?.join().includes('pyload_session')) {
|
||||
// Support pyload-ng, see https://github.com/benphelps/homepage/issues/517
|
||||
cache.put(isNgCacheKey, true);
|
||||
cache.put(`${isNgCacheKey}.${service}`, true);
|
||||
const sessionCookie = responseHeaders['set-cookie'][0];
|
||||
cache.put(sessionCacheKey, sessionCookie, 60 * 60 * 23 * 1000); // cache for 23h
|
||||
cache.put(`${sessionCacheKey}.${service}`, sessionCookie, 60 * 60 * 23 * 1000); // cache for 23h
|
||||
} else {
|
||||
cache.put(sessionCacheKey, sessionId);
|
||||
cache.put(`${sessionCacheKey}.${service}`, sessionId);
|
||||
}
|
||||
|
||||
return sessionId;
|
||||
@@ -72,14 +72,14 @@ export default async function pyloadProxyHandler(req, res) {
|
||||
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
|
||||
const loginUrl = `${widget.url}/api/login`;
|
||||
|
||||
let sessionId = cache.get(sessionCacheKey) ?? await login(loginUrl, widget.username, widget.password);
|
||||
let [status, data] = await fetchFromPyloadAPI(url, sessionId);
|
||||
let sessionId = cache.get(`${sessionCacheKey}.${service}`) ?? await login(loginUrl, service, widget.username, widget.password);
|
||||
let [status, data] = await fetchFromPyloadAPI(url, sessionId, null, service);
|
||||
|
||||
if (status === 403 || status === 401) {
|
||||
logger.info('Failed to retrieve data from Pyload API, trying to login again...');
|
||||
cache.del(sessionCacheKey);
|
||||
sessionId = await login(loginUrl, widget.username, widget.password);
|
||||
[status, data] = await fetchFromPyloadAPI(url, sessionId);
|
||||
cache.del(`${sessionCacheKey}.${service}`);
|
||||
sessionId = await login(loginUrl, service, widget.username, widget.password);
|
||||
[status, data] = await fetchFromPyloadAPI(url, sessionId, null, service);
|
||||
}
|
||||
|
||||
if (data?.error || status !== 200) {
|
||||
|
||||
@@ -44,9 +44,9 @@ export default function Component({ service }) {
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block label="qbittorrent.leech" value={t("common.number", { value: leech })} />
|
||||
<Block label="qbittorrent.download" value={t("common.bitrate", { value: rateDl })} />
|
||||
<Block label="qbittorrent.download" value={t("common.bibyterate", { value: rateDl, decimals: 1 })} />
|
||||
<Block label="qbittorrent.seed" value={t("common.number", { value: completed })} />
|
||||
<Block label="qbittorrent.upload" value={t("common.bitrate", { value: rateUl })} />
|
||||
<Block label="qbittorrent.upload" value={t("common.bibyterate", { value: rateUl, decimals: 1 })} />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ export default function Component({ service }) {
|
||||
<Container service={service}>
|
||||
<Block
|
||||
label="speedtest.download"
|
||||
value={t("common.bitrate", { value: speedtestData.data.download * 1024 * 1024 })}
|
||||
value={t("common.bitrate", { value: speedtestData.data.download * 1000 * 1000 })}
|
||||
/>
|
||||
<Block label="speedtest.upload" value={t("common.bitrate", { value: speedtestData.data.upload * 1024 * 1024 })} />
|
||||
<Block label="speedtest.upload" value={t("common.bitrate", { value: speedtestData.data.upload * 1000 * 1000 })} />
|
||||
<Block
|
||||
label="speedtest.ping"
|
||||
value={t("common.ms", {
|
||||
|
||||
42
src/widgets/tdarr/component.jsx
Normal file
42
src/widgets/tdarr/component.jsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { useTranslation } from "next-i18next";
|
||||
|
||||
import Container from "components/services/widget/container";
|
||||
import Block from "components/services/widget/block";
|
||||
import useWidgetAPI from "utils/proxy/use-widget-api";
|
||||
|
||||
export default function Component({ service }) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { widget } = service;
|
||||
|
||||
const { data: tdarrData, error: tdarrError } = useWidgetAPI(widget);
|
||||
|
||||
if (tdarrError) {
|
||||
return <Container error={tdarrError} />;
|
||||
}
|
||||
|
||||
if (!tdarrData) {
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block label="tdarr.queue" />
|
||||
<Block label="tdarr.processed" />
|
||||
<Block label="tdarr.errored" />
|
||||
<Block label="tdarr.saved" />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
const queue = parseInt(tdarrData.table1Count, 10) + parseInt(tdarrData.table4Count, 10);
|
||||
const processed = parseInt(tdarrData.table2Count, 10) + parseInt(tdarrData.table5Count, 10);
|
||||
const errored = parseInt(tdarrData.table3Count, 10) + parseInt(tdarrData.table6Count, 10);
|
||||
const saved = parseFloat(tdarrData.sizeDiff, 10) * 1000000000;
|
||||
|
||||
return (
|
||||
<Container service={service}>
|
||||
<Block label="tdarr.queue" value={t("common.number", { value: queue })} />
|
||||
<Block label="tdarr.processed" value={t("common.number", { value: processed })} />
|
||||
<Block label="tdarr.errored" value={t("common.number", { value: errored })} />
|
||||
<Block label="tdarr.saved" value={t("common.bytes", { value: saved })} />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
48
src/widgets/tdarr/proxy.js
Normal file
48
src/widgets/tdarr/proxy.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import { httpProxy } from "utils/proxy/http";
|
||||
import { formatApiCall } from "utils/proxy/api-helpers";
|
||||
import getServiceWidget from "utils/config/service-helpers";
|
||||
import createLogger from "utils/logger";
|
||||
import widgets from "widgets/widgets";
|
||||
|
||||
const proxyName = "tdarrProxyHandler";
|
||||
const logger = createLogger(proxyName);
|
||||
|
||||
export default async function tdarrProxyHandler(req, res) {
|
||||
const { group, service, endpoint } = req.query;
|
||||
|
||||
if (!group || !service) {
|
||||
logger.debug("Invalid or missing service '%s' or group '%s'", service, group);
|
||||
return res.status(400).json({ error: "Invalid proxy service type" });
|
||||
}
|
||||
|
||||
const widget = await getServiceWidget(group, service);
|
||||
|
||||
if (!widget) {
|
||||
logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group);
|
||||
return res.status(400).json({ error: "Invalid proxy service type" });
|
||||
}
|
||||
|
||||
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
|
||||
|
||||
const [status, contentType, data] = await httpProxy(url, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
"data": {
|
||||
"collection": "StatisticsJSONDB",
|
||||
"mode": "getById",
|
||||
"docID": "statistics"
|
||||
},
|
||||
}),
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (status !== 200) {
|
||||
logger.error("Error getting data from Tdarr: %d. Data: %s", status, data);
|
||||
return res.status(500).send({error: {message:"Error getting data from Tdarr", url, data}});
|
||||
}
|
||||
|
||||
if (contentType) res.setHeader("Content-Type", contentType);
|
||||
return res.status(status).send(data);
|
||||
}
|
||||
8
src/widgets/tdarr/widget.js
Normal file
8
src/widgets/tdarr/widget.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import tdarrProxyHandler from "./proxy";
|
||||
|
||||
const widget = {
|
||||
api: "{url}/api/v2/cruddb",
|
||||
proxyHandler: tdarrProxyHandler,
|
||||
};
|
||||
|
||||
export default widget;
|
||||
@@ -25,12 +25,12 @@ export default async function transmissionProxyHandler(req, res) {
|
||||
return res.status(400).json({ error: "Invalid proxy service type" });
|
||||
}
|
||||
|
||||
let headers = cache.get(headerCacheKey);
|
||||
let headers = cache.get(`${headerCacheKey}.${service}`);
|
||||
if (!headers) {
|
||||
headers = {
|
||||
"content-type": "application/json",
|
||||
}
|
||||
cache.put(headerCacheKey, headers);
|
||||
cache.put(`${headerCacheKey}.${service}`, headers);
|
||||
}
|
||||
|
||||
const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
|
||||
@@ -55,7 +55,7 @@ export default async function transmissionProxyHandler(req, res) {
|
||||
if (status === 409) {
|
||||
logger.debug("Transmission is rejecting the request, but returning a CSRF token");
|
||||
headers[csrfHeaderName] = responseHeaders[csrfHeaderName];
|
||||
cache.put(headerCacheKey, headers);
|
||||
cache.put(`${headerCacheKey}.${service}`, headers);
|
||||
|
||||
// retry the request, now with the CSRF token
|
||||
[status, contentType, data, responseHeaders] = await httpProxy(url, {
|
||||
|
||||
@@ -58,6 +58,7 @@ async function login(widget) {
|
||||
|
||||
export default async function unifiProxyHandler(req, res) {
|
||||
const widget = await getWidget(req);
|
||||
const { service } = req.query;
|
||||
if (!widget) {
|
||||
return res.status(400).json({ error: "Invalid proxy service type" });
|
||||
}
|
||||
@@ -68,7 +69,7 @@ export default async function unifiProxyHandler(req, res) {
|
||||
}
|
||||
|
||||
let [status, contentType, data, responseHeaders] = [];
|
||||
let prefix = cache.get(prefixCacheKey);
|
||||
let prefix = cache.get(`${prefixCacheKey}.${service}`);
|
||||
if (prefix === null) {
|
||||
// auto detect if we're talking to a UDM Pro, and cache the result so that we
|
||||
// don't make two requests each time data from Unifi is required
|
||||
@@ -77,7 +78,7 @@ export default async function unifiProxyHandler(req, res) {
|
||||
if (responseHeaders?.["x-csrf-token"]) {
|
||||
prefix = udmpPrefix;
|
||||
}
|
||||
cache.put(prefixCacheKey, prefix);
|
||||
cache.put(`${prefixCacheKey}.${service}`, prefix);
|
||||
}
|
||||
|
||||
widget.prefix = prefix;
|
||||
|
||||
@@ -16,7 +16,9 @@ import jackett from "./jackett/widget";
|
||||
import jellyseerr from "./jellyseerr/widget";
|
||||
import lidarr from "./lidarr/widget";
|
||||
import mastodon from "./mastodon/widget";
|
||||
import miniflux from "./miniflux/widget";
|
||||
import navidrome from "./navidrome/widget";
|
||||
import nextdns from "./nextdns/widget";
|
||||
import npm from "./npm/widget";
|
||||
import nzbget from "./nzbget/widget";
|
||||
import ombi from "./ombi/widget";
|
||||
@@ -38,6 +40,7 @@ import sonarr from "./sonarr/widget";
|
||||
import speedtest from "./speedtest/widget";
|
||||
import strelaysrv from "./strelaysrv/widget";
|
||||
import tautulli from "./tautulli/widget";
|
||||
import tdarr from "./tdarr/widget";
|
||||
import traefik from "./traefik/widget";
|
||||
import transmission from "./transmission/widget";
|
||||
import tubearchivist from "./tubearchivist/widget";
|
||||
@@ -65,7 +68,9 @@ const widgets = {
|
||||
jellyseerr,
|
||||
lidarr,
|
||||
mastodon,
|
||||
miniflux,
|
||||
navidrome,
|
||||
nextdns,
|
||||
npm,
|
||||
nzbget,
|
||||
ombi,
|
||||
@@ -87,6 +92,7 @@ const widgets = {
|
||||
speedtest,
|
||||
strelaysrv,
|
||||
tautulli,
|
||||
tdarr,
|
||||
traefik,
|
||||
transmission,
|
||||
tubearchivist,
|
||||
|
||||
Reference in New Issue
Block a user