Compare commits

...

8 Commits

Author SHA1 Message Date
shamoon
1ed2f16cbf Enhancement: improve dual-stack support (#6070) 2025-12-10 08:26:58 -08:00
github-actions[bot]
ba2b3eeb9e New Crowdin translations by GitHub Action (#5970) 2025-12-10 07:35:47 -08:00
shamoon
45ca94eb6d Use ComboboxInput 2025-12-10 07:34:26 -08:00
shamoon
a07cc25441 Fix: restore clickable suggestions in search widget (#6069) 2025-12-10 07:32:29 -08:00
Erv Walter
0dcd25d5ad Feature: Pangolin service widget (#6065)
Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com>
2025-12-08 13:30:08 -08:00
shamoon
cd34796b9d Merge branch 'main' into dev
Some checks failed
Docker CI / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
2025-12-07 08:53:57 -08:00
shamoon
0d788e3d06 Documentation: note instructions for enabling IPv6 in Docker
Some checks failed
Crowdin Action / Crowdin Sync (push) Has been cancelled
Docker CI / Linting Checks (push) Has been cancelled
Docs / Linting Checks (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
Docs / Test Build Docs (push) Has been cancelled
Docs / Build & Deploy Docs (push) Has been cancelled
Repository Maintenance / Stale (push) Has been cancelled
Repository Maintenance / Lock Old Threads (push) Has been cancelled
Repository Maintenance / Close Answered Discussions (push) Has been cancelled
Repository Maintenance / Close Outdated Discussions (push) Has been cancelled
Repository Maintenance / Close Unsupported Feature Requests (push) Has been cancelled
2025-12-07 08:49:27 -08:00
shamoon
8887281246 Documentation: make docker.sock mount read-only in docs
Some checks failed
Crowdin Action / Crowdin Sync (push) Has been cancelled
Docs / Linting Checks (push) Has been cancelled
Docker CI / Linting Checks (push) Has been cancelled
Docs / Test Build Docs (push) Has been cancelled
Docs / Build & Deploy Docs (push) Has been cancelled
Docker CI / Docker Build & Push (push) Has been cancelled
Repository Maintenance / Stale (push) Has been cancelled
Repository Maintenance / Lock Old Threads (push) Has been cancelled
Repository Maintenance / Close Answered Discussions (push) Has been cancelled
Repository Maintenance / Close Outdated Discussions (push) Has been cancelled
Repository Maintenance / Close Unsupported Feature Requests (push) Has been cancelled
2025-11-30 20:04:42 -08:00
56 changed files with 725 additions and 156 deletions

View File

@@ -54,7 +54,7 @@ RUN apk add --no-cache su-exec iputils-ping shadow
USER root
ENV NODE_ENV=production
ENV HOSTNAME=0.0.0.0
ENV HOSTNAME=::
ENV PORT=3000
EXPOSE $PORT

View File

@@ -12,6 +12,15 @@ export PGID=${PGID:-0}
export HOMEPAGE_BUILDTIME=$(date +%s)
# Try IPv6 first (dual stack when available), but fall back to IPv4 if the bind fails
export HOSTNAME=${HOSTNAME:-::}
if [ "$HOSTNAME" = "::" ]; then
if ! node -e "const server = require('http').createServer(() => {}); const host = '::'; const port = process.env.PORT || 3000; server.once('error', (err) => { console.error('IPv6 bind failed:', err.message); process.exit(1); }); server.listen(port, host, () => server.close(() => process.exit(0)));"; then
echo "Falling back to IPv4 bind at 0.0.0.0"
export HOSTNAME=0.0.0.0
fi
fi
# Check ownership before chown
if [ -e /app/config ]; then
CURRENT_UID=$(stat -c %u /app/config)

View File

@@ -14,7 +14,7 @@ services:
- 3000:3000
volumes:
- /path/to/config:/app/config # Make sure your local config directory exists
- /var/run/docker.sock:/var/run/docker.sock # (optional) For docker integrations
- /var/run/docker.sock:/var/run/docker.sock:ro # (optional) For docker integrations
environment:
HOMEPAGE_ALLOWED_HOSTS: gethomepage.dev # required, may need port. See gethomepage.dev/installation/#homepage_allowed_hosts
```
@@ -36,7 +36,7 @@ services:
- 3000:3000
volumes:
- /path/to/config:/app/config # Make sure your local config directory exists
- /var/run/docker.sock:/var/run/docker.sock # (optional) For docker integrations, see alternative methods
- /var/run/docker.sock:/var/run/docker.sock:ro # (optional) For docker integrations, see alternative methods
environment:
HOMEPAGE_ALLOWED_HOSTS: gethomepage.dev # required, may need port. See gethomepage.dev/installation/#homepage_allowed_hosts
PUID: $PUID

View File

@@ -68,7 +68,7 @@ All service widgets work essentially the same, that is, homepage makes a proxied
If, after correctly adding and mapping your custom icons via the [Icons](../configs/services.md#icons) instructions, you are still unable to see your icons please try recreating your container.
## Disabling IPv6
## Disabling IPv6 for http requests {#disabling-ipv6}
If you are having issues with certain widgets that are unable to reach public APIs (e.g. weather), in certain setups you may need to disable IPv6. You can set the environment variable `HOMEPAGE_PROXY_DISABLE_IPV6` to `true` to disable IPv6 for the homepage proxy.

View File

@@ -0,0 +1,29 @@
---
title: Pangolin
description: Pangolin Widget Configuration
---
Learn more about [Pangolin](https://github.com/fosrl/pangolin).
This widget shows sites (online/total), resources (healthy/total), targets (healthy/total), and traffic statistics for a Pangolin organization. A resource is considered healthy if at least one of its targets is healthy, or if it has no targets.
Allowed fields: `["sites", "resources", "targets", "traffic", "in", "out"]` (maximum of 4).
```yaml
widget:
type: pangolin
url: https://api.pangolin.net
key: your-api-key
org: your-org-id
```
Find your organization ID in the URL when logged in (e.g., `https://app.pangolin.net/{org-id}/...`).
## API Key Setup
Create an API key with the following permissions:
- **List Sites**
- **List Resources**
**Self-Hosted:** Enable the [Integration API](https://docs.pangolin.net/self-host/advanced/integration-api) in your Pangolin configuration before creating the key.

View File

@@ -122,6 +122,7 @@ nav:
- widgets/services/opnsense.md
- widgets/services/openwrt.md
- widgets/services/overseerr.md
- widgets/services/pangolin.md
- widgets/services/paperlessngx.md
- widgets/services/peanut.md
- widgets/services/pfsense.md

View File

@@ -599,6 +599,15 @@
"inbox": "Inmandjie",
"total": "Totaal"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Bronne",
"targets": "Teikens",
"traffic": "Verkeer",
"in": "In",
"out": "Uit"
},
"peanut": {
"battery_charge": "Batterylading",
"ups_load": "SVE-lading",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Vandag",
"gross_percent_1y": "Een jaar",
"gross_percent_max": "Alle tyd"
"gross_percent_max": "Alle tyd",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podsendinge",

View File

@@ -599,6 +599,15 @@
"inbox": "صندوق الوارد",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "شحن البطارية",
"ups_load": "حمل UPS",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "سنة",
"gross_percent_max": "كل الوقت"
"gross_percent_max": "كل الوقت",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "بودكاست",

View File

@@ -599,6 +599,15 @@
"inbox": "Входящи",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Заряд на батерията",
"ups_load": "Натоварване на UPS",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "Една година",
"gross_percent_max": "All time"
"gross_percent_max": "All time",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Подкасти",

View File

@@ -599,6 +599,15 @@
"inbox": "Safata d'entrada",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Càrrega de la bateria",
"ups_load": "Càrrega del SAI",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "Un any",
"gross_percent_max": "Sempre"
"gross_percent_max": "Sempre",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Pòdcasts",

View File

@@ -599,6 +599,15 @@
"inbox": "Doručená pošta",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Úroveň baterie",
"ups_load": "Zítěž UPS",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "Jeden rok",
"gross_percent_max": "Za celou dobu"
"gross_percent_max": "Za celou dobu",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasty",

View File

@@ -599,6 +599,15 @@
"inbox": "Indbakke",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Batteriniveau",
"ups_load": "UPS Load",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "Et År",
"gross_percent_max": "Altid"
"gross_percent_max": "Altid",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasts",

View File

@@ -599,6 +599,15 @@
"inbox": "Posteingang",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Akkuladung",
"ups_load": "USV-Auslastung",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Heute",
"gross_percent_1y": "Ein Jahr",
"gross_percent_max": "Gesamt"
"gross_percent_max": "Gesamt",
"net_worth": ""
},
"audiobookshelf": {
"podcasts": "Podcasts",

View File

@@ -599,6 +599,15 @@
"inbox": "Εισερχόμενα",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Battery Charge",
"ups_load": "UPS Load",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "Ένας χρόνος",
"gross_percent_max": "Διαχρονικά"
"gross_percent_max": "Διαχρονικά",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasts",

View File

@@ -599,6 +599,15 @@
"inbox": "Inbox",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Battery Charge",
"ups_load": "UPS Load",

View File

@@ -599,6 +599,15 @@
"inbox": "Inbox",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Battery Charge",
"ups_load": "UPS Load",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "One year",
"gross_percent_max": "All time"
"gross_percent_max": "All time",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasts",

View File

@@ -599,6 +599,15 @@
"inbox": "Bandeja de entrada",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Carga de la batería",
"ups_load": "Carga del UPS",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Hoy",
"gross_percent_1y": "Un año",
"gross_percent_max": "Todo el tiempo"
"gross_percent_max": "Todo el tiempo",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasts",

View File

@@ -599,6 +599,15 @@
"inbox": "Inbox",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Battery Charge",
"ups_load": "UPS Load",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "One year",
"gross_percent_max": "All time"
"gross_percent_max": "All time",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasts",

View File

@@ -599,6 +599,15 @@
"inbox": "Inbox",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Battery Charge",
"ups_load": "UPS Load",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "One year",
"gross_percent_max": "All time"
"gross_percent_max": "All time",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasts",

View File

@@ -142,8 +142,8 @@
"connectionStatusDisconnected": "Déconnecté",
"connectionStatusConnected": "Connecté",
"uptime": "Démarré depuis",
"maxDown": "Réception max.",
"maxUp": "Envoi max.",
"maxDown": "Réception max",
"maxUp": "Envoi max",
"down": "Réception",
"up": "Envoi",
"received": "Reçu",
@@ -229,7 +229,7 @@
"seed": "En partage"
},
"develancacheui": {
"cachehitbytes": "Cache Hit (B)",
"cachehitbytes": "Octets acquis du cache",
"cachemissbytes": "Cache Miss (B)"
},
"downloadstation": {
@@ -294,7 +294,7 @@
"queries": "Requêtes",
"blocked": "Bloqué",
"blocked_percent": "% bloqué",
"gravity": "Listes dom. bloqués"
"gravity": "Listes dom. Bloqués"
},
"adguard": {
"queries": "Requêtes",
@@ -599,6 +599,15 @@
"inbox": "Boîte de réception",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Charge de la batterie",
"ups_load": "Charge de lASI",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Aujourd'hui",
"gross_percent_1y": "Un an",
"gross_percent_max": "Depuis le début"
"gross_percent_max": "Depuis le début",
"net_worth": "Patrimoine net"
},
"audiobookshelf": {
"podcasts": "Podcasts",
@@ -1091,7 +1101,7 @@
"NEW_ARRAY": "Nouveau tableau",
"RECON_DISK": "Reconstruction du disque",
"DISABLE_DISK": "Disque désactivé",
"SWAP_DSBL": "Swap Disable",
"SWAP_DSBL": "Désactiver le swap",
"INVALID_EXPANSION": "Extension invalide",
"PARITY_NOT_BIGGEST": "La parité n'est pas la plus grande",
"TOO_MANY_MISSING_DISKS": "Trop de disques manquants",

View File

@@ -599,6 +599,15 @@
"inbox": "תיבת דואר נכנס",
"total": "סה\"כ"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "טעינת סוללה",
"ups_load": "עומס UPS",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "היום",
"gross_percent_1y": "שנה",
"gross_percent_max": "כל הזמן"
"gross_percent_max": "כל הזמן",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "פודקאסטים",

View File

@@ -599,6 +599,15 @@
"inbox": "Inbox",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Battery Charge",
"ups_load": "UPS Load",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "One year",
"gross_percent_max": "All time"
"gross_percent_max": "All time",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasts",

View File

@@ -599,6 +599,15 @@
"inbox": "Ulazni sandučić",
"total": "Ukupno"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Napunjenost baterije",
"ups_load": "UPS opterećenje",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Danas",
"gross_percent_1y": "Jedna godina",
"gross_percent_max": "Svo vrijeme"
"gross_percent_max": "Svo vrijeme",
"net_worth": "Neto vrijednost"
},
"audiobookshelf": {
"podcasts": "Podcasti",

View File

@@ -599,6 +599,15 @@
"inbox": "Beérkezett",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Akku töltöttsége",
"ups_load": "UPS terheltsége",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "Egy év",
"gross_percent_max": "Mindig"
"gross_percent_max": "Mindig",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcast",

View File

@@ -599,6 +599,15 @@
"inbox": "Kotak Masuk",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Sisa Baterai",
"ups_load": "Beban UPS",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "Satu Tahun",
"gross_percent_max": "Sepanjang Masa"
"gross_percent_max": "Sepanjang Masa",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcast",

View File

@@ -168,7 +168,7 @@
"passes": "Tessere"
},
"tautulli": {
"playing": "Playing",
"playing": "In riproduzione",
"transcoding": "Transcoding",
"bitrate": "Bitrate",
"no_active": "No Active Streams",
@@ -313,7 +313,7 @@
"total": "Total"
},
"suwayomi": {
"download": "Downloaded",
"download": "Scaricati",
"nondownload": "Non Scaricato",
"read": "Read",
"unread": "Unread",
@@ -599,6 +599,15 @@
"inbox": "In arrivo",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Carica Batteria",
"ups_load": "Carico UPS",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "Un anno",
"gross_percent_max": "Sempre"
"gross_percent_max": "Sempre",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcast",

View File

@@ -69,7 +69,7 @@
"docker": {
"rx": "受信済み",
"tx": "送信済み",
"mem": "MEM",
"mem": "メモリ",
"cpu": "CPU",
"running": "起動中",
"offline": "オフライン",
@@ -83,7 +83,7 @@
"partial": "部分的"
},
"ping": {
"error": "Error",
"error": "エラー",
"ping": "Ping",
"down": "下へ",
"up": "稼働",
@@ -112,7 +112,7 @@
"offline_alt": "オフライン",
"online": "オンライン",
"total": "Total",
"unknown": "Unknown"
"unknown": "不明"
},
"evcc": {
"pv_power": "発電量",
@@ -223,8 +223,8 @@
"invalid": "無効"
},
"deluge": {
"download": "Download",
"upload": "Upload",
"download": "ダウンロード",
"upload": "アップロード",
"leech": "Leech",
"seed": "Seed"
},
@@ -233,8 +233,8 @@
"cachemissbytes": "キャッシュミスバイト"
},
"downloadstation": {
"download": "Download",
"upload": "Upload",
"download": "ダウンロード",
"upload": "アップロード",
"leech": "Leech",
"seed": "Seed"
},
@@ -251,7 +251,7 @@
"queued": "Queued",
"movies": "Movies",
"queue": "Queue",
"unknown": "Unknown"
"unknown": "不明"
},
"lidarr": {
"wanted": "Wanted",
@@ -599,6 +599,15 @@
"inbox": "受信トレイ",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "バッテリー充電",
"ups_load": "UPS 負荷",
@@ -692,8 +701,8 @@
},
"diskstation": {
"days": "Days",
"uptime": "Uptime",
"volumeAvailable": "Available"
"uptime": "稼働時間",
"volumeAvailable": "利用可能"
},
"mylar": {
"series": "Series",
@@ -754,12 +763,13 @@
"gatus": {
"up": "Sites Up",
"down": "Sites Down",
"uptime": "Uptime"
"uptime": "稼働時間"
},
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "1年",
"gross_percent_max": "全期間"
"gross_percent_max": "全期間",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "ポッドキャスト",

View File

@@ -599,6 +599,15 @@
"inbox": "받은 편지함",
"total": "전체"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "배터리 충전",
"ups_load": "UPS 부하",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "오늘",
"gross_percent_1y": "1년",
"gross_percent_max": "전체 기간"
"gross_percent_max": "전체 기간",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "팟캐스트",

View File

@@ -599,6 +599,15 @@
"inbox": "Inbox",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Battery Charge",
"ups_load": "UPS Load",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "One year",
"gross_percent_max": "All time"
"gross_percent_max": "All time",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasts",

View File

@@ -599,6 +599,15 @@
"inbox": "Peti Masuk",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Bateri dicas",
"ups_load": "Beban UPS",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "Satu tahun",
"gross_percent_max": "Sepanjang masa"
"gross_percent_max": "Sepanjang masa",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podkas",

View File

@@ -599,6 +599,15 @@
"inbox": "Postvak In",
"total": "Totaal"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Batterij opladen",
"ups_load": "UPS-belasting",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Vandaag",
"gross_percent_1y": "Een jaar",
"gross_percent_max": "Altijd"
"gross_percent_max": "Altijd",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasts",

View File

@@ -599,6 +599,15 @@
"inbox": "Innboks",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Batteriladning",
"ups_load": "UPS last",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "Ett år",
"gross_percent_max": "Gjennom tidene"
"gross_percent_max": "Gjennom tidene",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podkaster",

View File

@@ -599,6 +599,15 @@
"inbox": "Skrzynka odbiorcza",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Stan baterii",
"ups_load": "Obciążenie UPS",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Dzisiaj",
"gross_percent_1y": "Rok",
"gross_percent_max": "Od początku"
"gross_percent_max": "Od początku",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasty",

View File

@@ -599,6 +599,15 @@
"inbox": "Caixa de entrada",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Carga da bateria",
"ups_load": "Carga da UPS",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "Um ano",
"gross_percent_max": "Desde Sempre"
"gross_percent_max": "Desde Sempre",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasts",

View File

@@ -599,6 +599,15 @@
"inbox": "Caixa de entrada",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Carga da bateria",
"ups_load": "Carga do UPS",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Hoje",
"gross_percent_1y": "Um ano",
"gross_percent_max": "Todo o tempo"
"gross_percent_max": "Todo o tempo",
"net_worth": "Patrimônio Líquido"
},
"audiobookshelf": {
"podcasts": "Podcasts",

View File

@@ -599,6 +599,15 @@
"inbox": "Inbox",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Încărcare Baterie",
"ups_load": "UPS Load",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "Un an",
"gross_percent_max": "Tot timpul"
"gross_percent_max": "Tot timpul",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasturi",

View File

@@ -193,7 +193,7 @@
"tv": "Сериалы"
},
"sabnzbd": {
"rate": "Rate",
"rate": "",
"queue": "Очередь",
"timeleft": "Осталось"
},
@@ -599,6 +599,15 @@
"inbox": "Входящие",
"total": "Всего"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Заряд батареи",
"ups_load": "Нагрузка на UPS",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Сегодня",
"gross_percent_1y": "Один год",
"gross_percent_max": "Все время"
"gross_percent_max": "Все время",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Подкасты",
@@ -1088,7 +1098,7 @@
"unraid": {
"STARTED": "Started",
"STOPPED": "Stopped",
"NEW_ARRAY": "New Array",
"NEW_ARRAY": "Новый массив",
"RECON_DISK": "Reconstructing Disk",
"DISABLE_DISK": "Disk Disabled",
"SWAP_DSBL": "Swap Disable",
@@ -1097,8 +1107,8 @@
"TOO_MANY_MISSING_DISKS": "Too Many Missing Disks",
"NEW_DISK_TOO_SMALL": "New Disk Too Small",
"NO_DATA_DISKS": "No Data Disks",
"notifications": "Notifications",
"status": "Status",
"notifications": "Уведомления",
"status": "Статус",
"cpu": "CPU",
"memoryUsed": "Memory Used",
"memoryAvailable": "Memory Available",

View File

@@ -362,8 +362,8 @@
},
"trilium": {
"version": "Verzia",
"notesCount": "Notes",
"dbSize": "Database Size",
"notesCount": "Poznámky",
"dbSize": "Veľkosť databázy",
"unknown": "Neznáme"
},
"navidrome": {
@@ -599,6 +599,15 @@
"inbox": "Schránka správ",
"total": "Celkom"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Nabitie batérie",
"ups_load": "Záťaž UPS",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Dnes",
"gross_percent_1y": "Jeden rok",
"gross_percent_max": "Za celý čas"
"gross_percent_max": "Za celý čas",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasty",
@@ -786,7 +796,7 @@
"downloadCount": "Poradie",
"downloadBytesRemaining": "Zostávajúce",
"downloadTotalBytes": "Veľkosť",
"downloadSpeed": "Speed"
"downloadSpeed": "Rýchlosť"
},
"kavita": {
"seriesCount": "Series",
@@ -952,7 +962,7 @@
"loading": "Načítava sa",
"open": "Open - US Market",
"closed": "Closed - US Market",
"invalidConfiguration": "Invalid Configuration"
"invalidConfiguration": "Neplatná konfigurácia"
},
"frigate": {
"cameras": "Kamery",
@@ -1022,10 +1032,10 @@
"loading": "Načítava sa"
},
"gitlab": {
"groups": "Groups",
"issues": "Issues",
"groups": "Skupiny",
"issues": "Problémy",
"merges": "Merge Requests",
"projects": "Projects"
"projects": "Projekty"
},
"apcups": {
"status": "Stav",
@@ -1035,7 +1045,7 @@
},
"karakeep": {
"bookmarks": "Bookmarks",
"favorites": "Favorites",
"favorites": "Obľúbené",
"archived": "Archived",
"highlights": "Highlights",
"lists": "Zoznamy",
@@ -1065,13 +1075,13 @@
"komodo": {
"total": "Celkom",
"running": "Beží",
"stopped": "Stopped",
"stopped": "Zastavené",
"down": "Down",
"unhealthy": "Nezdravý",
"unknown": "Neznáme",
"servers": "Servers",
"servers": "Servery",
"stacks": "Stacks",
"containers": "Containers"
"containers": "Kontajnery"
},
"filebrowser": {
"available": "Dostupné",
@@ -1080,8 +1090,8 @@
},
"wallos": {
"activeSubscriptions": "Subscriptions",
"thisMonthlyCost": "This Month",
"nextMonthlyCost": "Next Month",
"thisMonthlyCost": "Tento mesiac",
"nextMonthlyCost": "Ďalší mesiac",
"previousMonthlyCost": "Prev. Month",
"nextRenewingSubscription": "Next Payment"
},

View File

@@ -599,6 +599,15 @@
"inbox": "Prejeto",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Napolnjenost baterije",
"ups_load": "UPS obremenitev",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "Eno leto",
"gross_percent_max": "Celoten čas"
"gross_percent_max": "Celoten čas",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasti",

View File

@@ -599,6 +599,15 @@
"inbox": "Примљено",
"total": "Укупно"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Напуњеност батерије",
"ups_load": "Оптерећење УПС-а",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Данас",
"gross_percent_1y": "Једна година",
"gross_percent_max": "Све време"
"gross_percent_max": "Све време",
"net_worth": "Нето вредност"
},
"audiobookshelf": {
"podcasts": "Подкасти",

View File

@@ -599,6 +599,15 @@
"inbox": "Inbox",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Battery Charge",
"ups_load": "UPS Load",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "One year",
"gross_percent_max": "All time"
"gross_percent_max": "All time",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasts",

View File

@@ -599,6 +599,15 @@
"inbox": "Inbox",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Battery Charge",
"ups_load": "UPS Load",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "One year",
"gross_percent_max": "All time"
"gross_percent_max": "All time",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasts",

View File

@@ -599,6 +599,15 @@
"inbox": "Inbox",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Battery Charge",
"ups_load": "UPS Load",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "One year",
"gross_percent_max": "All time"
"gross_percent_max": "All time",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasts",

View File

@@ -599,6 +599,15 @@
"inbox": "Gelen Kutusu",
"total": "Toplam"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Pil Yüzdesi",
"ups_load": "UPS Yükü",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Bugün",
"gross_percent_1y": "Bir yıl",
"gross_percent_max": "Tüm zaman"
"gross_percent_max": "Tüm zaman",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcast",

View File

@@ -63,14 +63,14 @@
"wlan_users": "WLAN користувачі",
"up": "UP",
"down": "Завантаження",
"wait": "Please wait",
"wait": "Будь ласка, зачекайте",
"empty_data": "Статус підсистеми невідомий"
},
"docker": {
"rx": "RX",
"tx": "TX",
"mem": "MEM",
"cpu": "CPU",
"mem": "Пам'ять",
"cpu": "Процесор",
"running": "Запущено",
"offline": "Офлайн",
"error": "Помилка",
@@ -83,7 +83,7 @@
"partial": "Частковий"
},
"ping": {
"error": "Error",
"error": "Помилка",
"ping": "Пінг",
"down": "Офлайн",
"up": "Онлайн",
@@ -91,11 +91,11 @@
},
"siteMonitor": {
"http_status": "HTTP статус",
"error": "Error",
"error": "Помилка",
"response": "Відповідь",
"down": "Down",
"up": "Up",
"not_available": "Not Available"
"down": "Офлайн",
"up": "Онлайн",
"not_available": "Не доступний"
},
"emby": {
"playing": "Відтворення",
@@ -108,10 +108,10 @@
"songs": "Пісні"
},
"esphome": {
"offline": "Offline",
"offline_alt": "Offline",
"offline": "Офлайн",
"offline_alt": "Офлайн",
"online": "Онлайн",
"total": "Total",
"total": "Усього",
"unknown": "Невідомо"
},
"evcc": {
@@ -133,7 +133,7 @@
"unread": "Не прочитано"
},
"fritzbox": {
"connectionStatus": "Status",
"connectionStatus": "Стан",
"connectionStatusUnconfigured": "Не налаштовано",
"connectionStatusConnecting": "Підключення",
"connectionStatusAuthenticating": "Автентифікація",
@@ -141,11 +141,11 @@
"connectionStatusDisconnecting": "Відключення",
"connectionStatusDisconnected": "Відключено",
"connectionStatusConnected": "З'єднано",
"uptime": "Uptime",
"uptime": "Час роботи",
"maxDown": "Макс. завантаження",
"maxUp": "Макс. віддача",
"down": "Down",
"up": "Up",
"down": "Офлайн",
"up": "Онлайн",
"received": "Отримано",
"sent": "Надіслано",
"externalIPAddress": "Зовнішній IP",
@@ -168,10 +168,10 @@
"passes": "Пропуски"
},
"tautulli": {
"playing": "Playing",
"transcoding": "Transcoding",
"bitrate": "Bitrate",
"no_active": "No Active Streams",
"playing": "Грає",
"transcoding": "Транскодування",
"bitrate": "Бітрейт",
"no_active": "Немає активних потоків",
"plex_connection_error": "Перевірте з'єднання Plex"
},
"omada": {
@@ -189,30 +189,30 @@
"plex": {
"streams": "Активні потоки",
"albums": "Альбоми",
"movies": "Movies",
"movies": "Фільми",
"tv": "TБ шоу"
},
"sabnzbd": {
"rate": "Rate",
"rate": "Швидкість",
"queue": "Черга",
"timeleft": "Залишилось"
},
"rutorrent": {
"active": "Активний",
"upload": "Upload",
"download": "Download"
"upload": "Вивантаж.",
"download": "Завантажено"
},
"transmission": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
"download": "Завантажено",
"upload": "Вивантаж.",
"leech": "Ліч",
"seed": "Сід"
},
"qbittorrent": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
"download": "Завантажити",
"upload": "Вивантаж.",
"leech": "Ліч",
"seed": "Сід"
},
"qnap": {
"cpuUsage": "Використання CPU",
@@ -225,8 +225,8 @@
"deluge": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
"leech": "Ліч",
"seed": "Сід"
},
"develancacheui": {
"cachehitbytes": "Кеш-хіт байт",
@@ -234,33 +234,33 @@
},
"downloadstation": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
"upload": "Вивантаж.",
"leech": "Ліч",
"seed": "Сід"
},
"sonarr": {
"wanted": "Розшукується",
"queued": "У черзі",
"series": "Series",
"queue": "Queue",
"unknown": "Unknown"
"series": "Серіали",
"queue": "Черга",
"unknown": "Невідомо"
},
"radarr": {
"wanted": "Wanted",
"wanted": "У бажаних",
"missing": "Відсутній",
"queued": "Queued",
"movies": "Movies",
"queue": "Queue",
"unknown": "Unknown"
"queued": "У черзі",
"movies": "Фільми",
"queue": "Черга",
"unknown": "Невідомо"
},
"lidarr": {
"wanted": "Wanted",
"queued": "Queued",
"wanted": "У бажаних",
"queued": "У черзі",
"artists": "Виконавці"
},
"readarr": {
"wanted": "Wanted",
"queued": "Queued",
"wanted": "У бажаних",
"queued": "У черзі",
"books": "Книжки"
},
"bazarr": {
@@ -273,20 +273,20 @@
"available": "Доступно"
},
"jellyseerr": {
"pending": "Pending",
"approved": "Approved",
"available": "Available",
"issues": "Open Issues"
"pending": "Очікує",
"approved": "Схвалено",
"available": "Доступно",
"issues": "Проблеми до усунення"
},
"overseerr": {
"pending": "Pending",
"pending": "Очікує",
"processing": "Обробка",
"approved": "Approved",
"available": "Available"
"approved": "Схвалено",
"available": "Доступно"
},
"netalertx": {
"total": "Total",
"connected": "Connected",
"total": "Усього",
"connected": "З'єднано",
"new_devices": "Нові пристрої",
"down_alerts": "Сповіщення про падіння"
},
@@ -297,13 +297,13 @@
"gravity": "Доменів в списку"
},
"adguard": {
"queries": "Queries",
"blocked": "Blocked",
"queries": "Запити",
"blocked": "Заблоковано",
"filtered": "Відфільтровано",
"latency": "Затримка"
},
"speedtest": {
"upload": "Upload",
"upload": "Вивантаж.",
"download": "Download",
"ping": "Ping"
},
@@ -599,6 +599,15 @@
"inbox": "Вхідні",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Заряд батареї",
"ups_load": "UPS завантаження",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "Один рік",
"gross_percent_max": "Весь час"
"gross_percent_max": "Весь час",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Подкасти",

View File

@@ -599,6 +599,15 @@
"inbox": "Inbox",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "Battery Charge",
"ups_load": "UPS Load",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "One year",
"gross_percent_max": "All time"
"gross_percent_max": "All time",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasts",

View File

@@ -599,6 +599,15 @@
"inbox": "收件箱",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "充電",
"ups_load": "備用電源負載",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "一年",
"gross_percent_max": "所有時間"
"gross_percent_max": "所有時間",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "播客",

View File

@@ -599,6 +599,15 @@
"inbox": "收件箱",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "充电中",
"ups_load": "UPS 负载",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "一年",
"gross_percent_max": "所有时间"
"gross_percent_max": "所有时间",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "播客",

View File

@@ -599,6 +599,15 @@
"inbox": "收件箱",
"total": "Total"
},
"pangolin": {
"orgs": "Orgs",
"sites": "Sites",
"resources": "Resources",
"targets": "Targets",
"traffic": "Traffic",
"in": "In",
"out": "Out"
},
"peanut": {
"battery_charge": "充電",
"ups_load": "備用電源負載",
@@ -759,7 +768,8 @@
"ghostfolio": {
"gross_percent_today": "Today",
"gross_percent_1y": "一年",
"gross_percent_max": "所有時間"
"gross_percent_max": "所有時間",
"net_worth": "Net Worth"
},
"audiobookshelf": {
"podcasts": "Podcasts",

View File

@@ -1,4 +1,14 @@
import { Combobox, Listbox, Transition } from "@headlessui/react";
import {
Combobox,
ComboboxInput,
ComboboxOption,
ComboboxOptions,
Listbox,
ListboxButton,
ListboxOption,
ListboxOptions,
Transition,
} from "@headlessui/react";
import classNames from "classnames";
import { useTranslation } from "next-i18next";
import { Fragment, useEffect, useState } from "react";
@@ -158,7 +168,7 @@ export default function Search({ options }) {
<div className="flex-col relative h-8 my-4 min-w-fit z-20">
<div className="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none w-full text-theme-800 dark:text-white" />
<Combobox value={query}>
<Combobox.Input
<ComboboxInput
type="text"
className="
overflow-hidden w-full h-full rounded-md
@@ -189,7 +199,7 @@ export default function Search({ options }) {
disabled={availableProviderIds?.length === 1}
>
<div>
<Listbox.Button
<ListboxButton
className="
absolute right-0.5 bottom-0.5 rounded-r-md px-4 py-2
text-white font-medium text-sm
@@ -198,7 +208,7 @@ export default function Search({ options }) {
>
<selectedProvider.icon className="text-white w-3 h-3" />
<span className="sr-only">{t("search.search")}</span>
</Listbox.Button>
</ListboxButton>
</div>
<Transition
as={Fragment}
@@ -209,7 +219,7 @@ export default function Search({ options }) {
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Listbox.Options
<ListboxOptions
className="absolute right-0 z-10 mt-1 origin-top-right rounded-md
bg-theme-100 dark:bg-theme-600 shadow-lg
ring-1 ring-black ring-opacity-5 focus:outline-hidden"
@@ -218,7 +228,7 @@ export default function Search({ options }) {
{availableProviderIds.map((providerId) => {
const p = searchProviders[providerId];
return (
<Listbox.Option key={providerId} value={p} as={Fragment}>
<ListboxOption key={providerId} value={p} as={Fragment}>
{({ active }) => (
<li
className={classNames(
@@ -229,23 +239,23 @@ export default function Search({ options }) {
<p.icon className="h-4 w-4 mx-4 my-2" />
</li>
)}
</Listbox.Option>
</ListboxOption>
);
})}
</div>
</Listbox.Options>
</ListboxOptions>
</Transition>
</Listbox>
{searchSuggestions[1]?.length > 0 && (
<Combobox.Options className="mt-1 rounded-md bg-theme-50 dark:bg-theme-800 border border-theme-300 dark:border-theme-200/30 cursor-pointer shadow-lg">
<ComboboxOptions className="mt-1 rounded-md bg-theme-50 dark:bg-theme-800 border border-theme-300 dark:border-theme-200/30 cursor-pointer shadow-lg">
<div className="p-1 bg-white/50 dark:bg-white/10 text-theme-900/90 dark:text-white/90 text-xs">
<Combobox.Option key={query} value={query} />
<ComboboxOption key={query} value={query} />
{searchSuggestions[1].map((suggestion) => (
<Combobox.Option
<ComboboxOption
key={suggestion}
value={suggestion}
onClick={() => {
onMouseDown={() => {
doSearch(suggestion);
}}
className="flex w-full"
@@ -266,10 +276,10 @@ export default function Search({ options }) {
</div>
);
}}
</Combobox.Option>
</ComboboxOption>
))}
</div>
</Combobox.Options>
</ComboboxOptions>
)}
</Combobox>
</div>

View File

@@ -4,7 +4,7 @@ export function middleware(req) {
// Check the Host header, if HOMEPAGE_ALLOWED_HOSTS is set
const host = req.headers.get("host");
const port = process.env.PORT || 3000;
let allowedHosts = [`localhost:${port}`, `127.0.0.1:${port}`];
let allowedHosts = [`localhost:${port}`, `127.0.0.1:${port}`, `[::1]:${port}`];
const allowAll = process.env.HOMEPAGE_ALLOWED_HOSTS === "*";
if (process.env.HOMEPAGE_ALLOWED_HOSTS) {
allowedHosts = allowedHosts.concat(process.env.HOMEPAGE_ALLOWED_HOSTS.split(","));

View File

@@ -53,6 +53,7 @@ export default async function credentialedProxyHandler(req, res, map) {
"linkwarden",
"mealie",
"netalertx",
"pangolin",
"tailscale",
"tandoor",
"pterodactyl",

View File

@@ -97,6 +97,7 @@ const components = {
openmediavault: dynamic(() => import("./openmediavault/component")),
openwrt: dynamic(() => import("./openwrt/component")),
paperlessngx: dynamic(() => import("./paperlessngx/component")),
pangolin: dynamic(() => import("./pangolin/component")),
pfsense: dynamic(() => import("./pfsense/component")),
photoprism: dynamic(() => import("./photoprism/component")),
proxmoxbackupserver: dynamic(() => import("./proxmoxbackupserver/component")),

View File

@@ -0,0 +1,70 @@
import Block from "components/services/widget/block";
import Container from "components/services/widget/container";
import { useTranslation } from "next-i18next";
import useWidgetAPI from "utils/proxy/use-widget-api";
const MAX_ALLOWED_FIELDS = 4;
export default function Component({ service }) {
const { t } = useTranslation();
const { widget } = service;
if (!widget.fields) {
widget.fields = ["sites", "resources", "targets", "traffic"];
} else if (widget.fields?.length > MAX_ALLOWED_FIELDS) {
widget.fields = widget.fields.slice(0, MAX_ALLOWED_FIELDS);
}
const { data: sitesData, error: sitesError } = useWidgetAPI(widget, "sites");
const { data: resourcesData, error: resourcesError } = useWidgetAPI(widget, "resources");
if (sitesError || resourcesError) {
return <Container service={service} error={sitesError || resourcesError} />;
}
if (!sitesData || !resourcesData) {
return (
<Container service={service}>
<Block label="pangolin.sites" />
<Block label="pangolin.resources" />
<Block label="pangolin.targets" />
<Block label="pangolin.traffic" />
<Block label="pangolin.in" />
<Block label="pangolin.out" />
</Container>
);
}
const sites = sitesData.data?.sites || [];
const resources = resourcesData.data?.resources || [];
const sitesTotal = sites.length;
const sitesOnline = sites.filter((s) => s.online).length;
const resourcesTotal = resources.length;
const resourcesHealthy = resources.filter(
(r) => r.targets?.some((t) => t.healthStatus !== "unhealthy") || !r.targets?.length,
).length;
const targetsTotal = resources.reduce((sum, r) => sum + (r.targets?.length || 0), 0);
const targetsHealthy = resources.reduce(
(sum, r) => sum + (r.targets?.filter((t) => t.healthStatus !== "unhealthy").length || 0),
0,
);
const trafficIn = sites.reduce((sum, s) => sum + (s.megabytesIn || 0), 0) * 1_000_000;
const trafficOut = sites.reduce((sum, s) => sum + (s.megabytesOut || 0), 0) * 1_000_000;
const trafficTotal = trafficIn + trafficOut;
return (
<Container service={service}>
<Block label="pangolin.sites" value={`${sitesOnline} / ${sitesTotal}`} />
<Block label="pangolin.resources" value={`${resourcesHealthy} / ${resourcesTotal}`} />
<Block label="pangolin.targets" value={`${targetsHealthy} / ${targetsTotal}`} />
<Block label="pangolin.traffic" value={t("common.bytes", { value: trafficTotal })} />
<Block label="pangolin.in" value={t("common.bytes", { value: trafficIn })} />
<Block label="pangolin.out" value={t("common.bytes", { value: trafficOut })} />
</Container>
);
}

View File

@@ -0,0 +1,17 @@
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
const widget = {
api: "{url}/v1/{endpoint}",
proxyHandler: credentialedProxyHandler,
mappings: {
sites: {
endpoint: "org/{org}/sites",
},
resources: {
endpoint: "org/{org}/resources",
},
},
};
export default widget;

View File

@@ -87,6 +87,7 @@ import openmediavault from "./openmediavault/widget";
import openwrt from "./openwrt/widget";
import opnsense from "./opnsense/widget";
import overseerr from "./overseerr/widget";
import pangolin from "./pangolin/widget";
import paperlessngx from "./paperlessngx/widget";
import peanut from "./peanut/widget";
import pfsense from "./pfsense/widget";
@@ -237,6 +238,7 @@ const widgets = {
openmediavault,
openwrt,
paperlessngx,
pangolin,
peanut,
pfsense,
photoprism,