Compare commits

..

32 Commits

Author SHA1 Message Date
shamoon
e1799f17e3 Merge pull request #777 from jhollowe-forks/truenas-apikey
Change TrueNAS to use API key
2023-01-07 11:05:43 -08:00
Michael Shamoon
730f1c5ec1 Support api key + username / pass for truenas widget 2023-01-07 10:00:25 -08:00
John Hollowell
34a7b25c9c Add truenas key to credentialed proxy handler
Revert "Change TrueNAS to use API key"

This reverts commit 1926c26b77d8e048d92da6e20ff24a3056237daf.

Co-Authored-By: John Hollowell <jhollowe@johnhollowell.com>
2023-01-07 10:00:16 -08:00
Anonymous
f93106970a Translated using Weblate (Japanese)
Currently translated at 5.2% (15 of 288 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ja/
2023-01-07 01:13:54 +01:00
SASAGAWA, Kiyoshi
2b4fb03dcf Translated using Weblate (Japanese)
Currently translated at 5.2% (15 of 288 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ja/
2023-01-06 01:14:21 +01:00
Anonymous
11fad11b6d Translated using Weblate (Japanese)
Currently translated at 100.0% (0 of 0 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ja/
2023-01-05 10:26:06 +01:00
Milo Ivir
885dca1750 Translated using Weblate (Croatian)
Currently translated at 97.9% (282 of 288 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hr/
2023-01-05 10:26:03 +01:00
ze cabra
1caa9faded Translated using Weblate (Portuguese)
Currently translated at 88.8% (256 of 288 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pt/
2023-01-05 10:26:03 +01:00
SASAGAWA, Kiyoshi
4f24c0f909 Added translation using Weblate (Japanese) 2023-01-05 10:25:44 +01:00
shamoon
1fd198c255 Merge pull request #771 from benphelps/datetime-locale
Feature: allow setting locale for datetime widget directly
2023-01-04 19:12:23 -08:00
Michael Shamoon
84b7f103c3 Allow setting locale for datetime widget directly 2023-01-04 13:53:06 -08:00
shamoon
272be2c086 Merge pull request #770 from HaroldVB/patch-1
Giving Read Only rights to homepage
2023-01-04 12:28:31 -08:00
Harold
6705197a35 Giving Read Only rights to homepage
Giving Read Only rights to homepage container.
Adding :RO to the docker.sock volume. When the container gets compromised the intruder will have root access basically. The container doesn't need the write privileges.

This measure will stop inexperienced people from exposing their docker.socket to the public internet.
2023-01-04 20:10:18 +01:00
shamoon
2ac06937f9 Merge pull request #769 from benphelps/downloadstation-support-v6+v7
Fix: Support Synology DownloadStation v6 + v7
2023-01-03 23:19:56 -08:00
Michael Shamoon
35a2cd9b94 Support Synology DownloadStation v6 + v7
Co-Authored-By: Benoit SERRA <11260343+Oupsman@users.noreply.github.com>
2023-01-03 15:51:21 -08:00
shamoon
3a43cf247b Merge pull request #765 from benphelps/fix-764
Use tabular nums for datetime to prevent size changing
2023-01-02 08:34:49 -08:00
Michael Shamoon
af29f5b266 Use tabular nums for datetime to prevent size changing 2023-01-02 08:30:57 -08:00
shamoon
f6b1304e22 Merge pull request #749 from benphelps/fix-docker-log-error
Fix: remove error on no discovered services
2022-12-30 20:32:11 -08:00
Michael Shamoon
ee729a7e6a remove error on no discovered services 2022-12-30 20:31:25 -08:00
Michael Shamoon
bc7937db71 omada widget cleanup 2022-12-29 00:25:50 -08:00
shamoon
0e1aeaf54c Merge pull request #719 from benphelps/docker-server-failovers
Fix: Handle docker server failures if others succeed
2022-12-28 18:40:08 -08:00
shamoon
2e8717247d Merge pull request #745 from benphelps/fix-version-check-cache
Fix: version check caching
2022-12-28 18:38:50 -08:00
Michael Shamoon
d17a17bd3c Use server-side endpoint to properly cache GH release data 2022-12-28 18:33:14 -08:00
Michael Shamoon
0afc1b96f1 CPU / memory / disk usage bars start from 0
Closes #737
2022-12-28 16:21:04 -08:00
Michael Shamoon
5fbc6702bc Prevent blocking error on GH releases failure
Closes #738
2022-12-28 16:17:49 -08:00
Nonoss117
75455a23e2 Translated using Weblate (French)
Currently translated at 100.0% (288 of 288 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fr/
2022-12-27 12:50:18 +01:00
gallegonovato
2aed46671f Translated using Weblate (Spanish)
Currently translated at 100.0% (288 of 288 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/es/
2022-12-27 12:50:18 +01:00
shamoon
88934ec39a Correct debug messages in Pyload widget
Closes #733
2022-12-26 06:07:43 -08:00
shamoon
21c0c687cd Update README.md 2022-12-26 01:17:54 -08:00
shamoon
9f63a5a1d5 Merge pull request #732 from benphelps/fix-update-checker
Fix broken update checker
2022-12-26 01:02:37 -08:00
Michael Shamoon
679704949e Fix broken update checker 2022-12-26 01:02:01 -08:00
Michael Shamoon
6b90d3ef28 Handle docker server failures if others succeed 2022-12-22 21:16:52 -08:00
19 changed files with 670 additions and 208 deletions

View File

@@ -45,15 +45,17 @@
- Container status (Running / Stopped) & statistics (CPU, Memory, Network)
- Automatic service discovery (via labels)
- 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, Authentik, Proxmox
- Sonarr, Radarr, Readarr, Prowlarr, Bazarr, Lidarr, Emby, Jellyfin, Tautulli, Plex and more
- Ombi, Overseerr, Jellyseerr, Jackett, NZBGet, SABnzbd, ruTorrent, Transmission, qBittorrent and more
- Portainer, Traefik, Speedtest Tracker, PiHole, AdGuard Home, Nginx Proxy Manager, Gotify, Syncthing Relay Server, Authentik, Proxmox and more
- Information Providers
- Coin Market Cap, Mastodon
- Coin Market Cap, Mastodon and more
- Information & Utility Widgets
- System Stats (Disk, CPU, Memory)
- Weather via [OpenWeatherMap](https://openweathermap.org/) or [Open-Meteo](https://open-meteo.com/)
- Search Bar
- Web Search Bar
- UniFi Console, Glances and more
- Instant "Quick-launch" search
- Customizable
- 21 theme colors with light and dark mode support
- Background image support
@@ -63,7 +65,7 @@
If you have any questions, suggestions, or general issues, please start a discussion on the [Discussions](https://github.com/benphelps/homepage/discussions) page.
If you have a more specific issue, please open an issue on the [Issues](https://github.com/benphelps/homepage/issues) page.
For bug reports, please open an issue on the [Issues](https://github.com/benphelps/homepage/issues) page.
## Getting Started
@@ -83,7 +85,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
```
or docker run:

View File

@@ -394,14 +394,14 @@
"numberOfLeases": "Alquileres"
},
"xteve": {
"streams_all": "All Streams",
"streams_active": "Active Streams",
"streams_xepg": "XEPG Channels"
"streams_all": "Todas las corrientes",
"streams_active": "Corrientes activas",
"streams_xepg": "Canales XEPG"
},
"opnsense": {
"cpu": "CPU Load",
"memory": "Active Memory",
"wanUpload": "WAN Upload",
"wanDownload": "WAN Download"
"cpu": "Carga de la CPU",
"memory": "Memoria activa",
"wanUpload": "Carga WAN",
"wanDownload": "Descargar WAN"
}
}

View File

@@ -399,9 +399,9 @@
"streams_xepg": "Canal XEPG"
},
"opnsense": {
"cpu": "CPU Load",
"memory": "Active Memory",
"wanUpload": "WAN Upload",
"wanDownload": "WAN Download"
"cpu": "Charge CPU",
"memory": "Mém. Utilisée",
"wanUpload": "WAN Envoi",
"wanDownload": "WAN Récep."
}
}

View File

@@ -347,61 +347,61 @@
"deluge": {
"download": "Preuzimanje",
"upload": "Prijenos",
"leech": "Leech",
"seed": "Seed"
"leech": "Korištenje tuđeg sadržaja",
"seed": "Prenošenje preuzetog sadržaja"
},
"flood": {
"download": "Preuzimanje",
"upload": "Prijenos",
"leech": "Leech",
"seed": "Seed"
"leech": "Korištenje tuđeg sadržaja",
"seed": "Prenošenje preuzetog sadržaja"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
"queue": "Red čekanja",
"processed": "Obrađeno",
"errored": "S greškom",
"saved": "Spremljeno"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
"read": "Pročitano",
"unread": "Nepročitano"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
"wait": "Pričekaj",
"no_devices": "Podaci o uređaju nisu primljeni"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
},
"omada": {
"connectedAp": "Connected APs",
"activeUser": "Active devices",
"alerts": "Alerts",
"connectedGateway": "Connected gateways",
"connectedSwitches": "Connected switches"
"connectedAp": "Povezani AP-ovi",
"activeUser": "Aktivni uređaji",
"alerts": "Upozorenja",
"connectedGateway": "Povezani pristupi",
"connectedSwitches": "Povezani prekidači"
},
"downloadstation": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
"download": "Preuzimanje",
"upload": "Prijenos",
"leech": "Korištenje tuđeg sadržaja",
"seed": "Prenošenje preuzetog sadržaja"
},
"mikrotik": {
"cpuLoad": "CPU Load",
"memoryUsed": "Memory Used",
"memoryUsed": "Korištena memorija",
"uptime": "Uptime",
"numberOfLeases": "Leases"
},
"xteve": {
"streams_all": "All Streams",
"streams_active": "Active Streams",
"streams_xepg": "XEPG Channels"
"streams_all": "Svi prijenosi",
"streams_active": "Aktivni prijenosi",
"streams_xepg": "XEPG kanali"
},
"opnsense": {
"cpu": "CPU Load",
"memory": "Active Memory",
"wanUpload": "WAN Upload",
"wanDownload": "WAN Download"
"memory": "Aktivna memorija",
"wanUpload": "WAN prijenos",
"wanDownload": "WAN preuzimanje"
}
}

View File

@@ -0,0 +1,407 @@
{
"navidrome": {
"nothing_streaming": "No Active Streams",
"please_wait": "Please Wait"
},
"npm": {
"total": "Total",
"enabled": "Enabled",
"disabled": "Disabled"
},
"strelaysrv": {
"numActiveSessions": "Sessions",
"numConnections": "Connections",
"dataRelayed": "Relayed",
"transferRate": "Rate"
},
"glances": {
"mem": "MEM",
"cpu": "CPU",
"wait": "Please wait"
},
"autobrr": {
"filters": "Filters",
"indexers": "Indexers",
"approvedPushes": "Approved",
"rejectedPushes": "Rejected"
},
"gluetun": {
"region": "Region",
"country": "Country",
"public_ip": "Public IP"
},
"common": {
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
},
"widget": {
"api_error": "APIエラー",
"information": "情報",
"missing_type": "見つからないウィジェットタイプ: {{type}}",
"status": "ステータス",
"url": "URL",
"raw_error": "生のエラー",
"response_data": "レスポンスデータ"
},
"weather": {
"current": "Current Location",
"allow": "Click to allow",
"updating": "Updating",
"wait": "Please wait"
},
"search": {
"placeholder": "Search…"
},
"resources": {
"cpu": "CPU",
"total": "Total",
"free": "Free",
"used": "Used",
"load": "Load"
},
"unifi": {
"users": "Users",
"uptime": "System Uptime",
"days": "Days",
"wan": "WAN",
"lan": "LAN",
"wlan": "WLAN",
"devices": "Devices",
"lan_devices": "LAN Devices",
"wlan_devices": "WLAN Devices",
"lan_users": "LAN Users",
"wlan_users": "WLAN Users",
"up": "UP",
"down": "DOWN",
"wait": "Please wait"
},
"docker": {
"rx": "RX",
"tx": "TX",
"mem": "MEM",
"cpu": "CPU",
"offline": "Offline",
"error": "Error",
"unknown": "Unknown"
},
"ping": {
"error": "Error",
"ping": "Ping"
},
"emby": {
"playing": "Playing",
"transcoding": "Transcoding",
"bitrate": "Bitrate",
"no_active": "No Active Streams"
},
"flood": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"changedetectionio": {
"totalObserved": "Total Observed",
"diffsDetected": "Diffs Detected"
},
"tautulli": {
"playing": "Playing",
"transcoding": "Transcoding",
"bitrate": "Bitrate",
"no_active": "No Active Streams"
},
"omada": {
"connectedAp": "Connected APs",
"activeUser": "Active devices",
"alerts": "Alerts",
"connectedGateway": "Connected gateways",
"connectedSwitches": "Connected switches"
},
"nzbget": {
"rate": "Rate",
"remaining": "Remaining",
"downloaded": "Downloaded"
},
"plex": {
"streams": "Active Streams",
"movies": "Movies",
"tv": "TV Shows"
},
"sabnzbd": {
"rate": "Rate",
"queue": "Queue",
"timeleft": "Time Left"
},
"rutorrent": {
"active": "Active",
"upload": "Upload",
"download": "Download"
},
"transmission": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"qbittorrent": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"deluge": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"downloadstation": {
"download": "Download",
"upload": "Upload",
"leech": "Leech",
"seed": "Seed"
},
"sonarr": {
"wanted": "Wanted",
"queued": "Queued",
"series": "Series"
},
"radarr": {
"wanted": "Wanted",
"missing": "Missing",
"queued": "Queued",
"movies": "Movies"
},
"lidarr": {
"wanted": "Wanted",
"queued": "Queued",
"albums": "Albums"
},
"readarr": {
"wanted": "Wanted",
"queued": "Queued",
"books": "Books"
},
"bazarr": {
"missingEpisodes": "Missing Episodes",
"missingMovies": "Missing Movies"
},
"ombi": {
"pending": "Pending",
"approved": "Approved",
"available": "Available"
},
"jellyseerr": {
"pending": "Pending",
"approved": "Approved",
"available": "Available"
},
"overseerr": {
"pending": "Pending",
"processing": "Processing",
"approved": "Approved",
"available": "Available"
},
"pihole": {
"queries": "Queries",
"blocked": "Blocked",
"gravity": "Gravity"
},
"adguard": {
"queries": "Queries",
"blocked": "Blocked",
"filtered": "Filtered",
"latency": "Latency"
},
"speedtest": {
"upload": "Upload",
"download": "Download",
"ping": "Ping"
},
"portainer": {
"running": "Running",
"stopped": "Stopped",
"total": "Total"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
},
"traefik": {
"routers": "Routers",
"services": "Services",
"middleware": "Middleware"
},
"coinmarketcap": {
"configure": "Configure one or more crypto currencies to track",
"1hour": "1 Hour",
"1day": "1 Day",
"7days": "7 Days",
"30days": "30 Days"
},
"gotify": {
"apps": "Applications",
"clients": "Clients",
"messages": "Messages"
},
"prowlarr": {
"enableIndexers": "Indexers",
"numberOfGrabs": "Grabs",
"numberOfQueries": "Queries",
"numberOfFailGrabs": "Fail Grabs",
"numberOfFailQueries": "Fail Queries"
},
"jackett": {
"configured": "Configured",
"errored": "Errored"
},
"mastodon": {
"user_count": "Users",
"status_count": "Posts",
"domain_count": "Domains"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
},
"authentik": {
"users": "Users",
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
},
"quicklaunch": {
"bookmark": "Bookmark",
"service": "Service"
},
"wmo": {
"0-day": "Sunny",
"1-day": "Mainly Sunny",
"0-night": "Clear",
"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",
"57-day": "Freezing Drizzle",
"57-night": "Freezing Drizzle",
"61-day": "Light Rain",
"61-night": "Light Rain",
"63-day": "Rain",
"63-night": "Rain",
"67-night": "Freezing Rain",
"71-day": "Light Snow",
"65-day": "Heavy Rain",
"65-night": "Heavy Rain",
"66-day": "Freezing Rain",
"66-night": "Freezing Rain",
"67-day": "Freezing Rain",
"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",
"95-day": "Thunderstorm",
"95-night": "Thunderstorm",
"96-day": "Thunderstorm With Hail",
"96-night": "Thunderstorm With Hail",
"99-day": "Thunderstorm With Hail",
"99-night": "Thunderstorm With Hail"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
},
"truenas": {
"load": "System Load",
"uptime": "Uptime",
"alerts": "Alerts",
"time": "{{value, number(style: unit; unitDisplay: long;)}}"
},
"pyload": {
"speed": "Speed",
"active": "Active",
"queue": "Queue",
"total": "Total"
},
"hdhomerun": {
"channels": "Channels",
"hd": "HD"
},
"scrutiny": {
"passed": "Passed",
"failed": "Failed",
"unknown": "Unknown"
},
"paperlessngx": {
"inbox": "Inbox",
"total": "Total"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
},
"mikrotik": {
"cpuLoad": "CPU Load",
"memoryUsed": "Memory Used",
"uptime": "Uptime",
"numberOfLeases": "Leases"
},
"xteve": {
"streams_all": "All Streams",
"streams_active": "Active Streams",
"streams_xepg": "XEPG Channels"
},
"opnsense": {
"cpu": "CPU Load",
"memory": "Active Memory",
"wanUpload": "WAN Upload",
"wanDownload": "WAN Download"
}
}

View File

@@ -1,6 +1,6 @@
{
"widget": {
"missing_type": "Widget ausente: {{type}}",
"missing_type": "Tipo de Widget ausente: {{type}}",
"api_error": "Erro da API",
"status": "Estado",
"information": "Informação",
@@ -14,8 +14,8 @@
"resources": {
"total": "Total",
"free": "Livre",
"used": "Usado",
"load": "Carregar",
"used": "Utilizado",
"load": "Carga",
"cpu": "CPU"
},
"docker": {
@@ -53,7 +53,7 @@
"wanted": "Desejado",
"queued": "Fila",
"movies": "Filmes",
"missing": "Missing"
"missing": "Faltando"
},
"readarr": {
"wanted": "Desejados",
@@ -111,14 +111,14 @@
"weather": {
"current": "Localização atual",
"allow": "Clicar para permitir",
"updating": "A atualizar",
"updating": "Atualizando",
"wait": "Por favor aguarde"
},
"overseerr": {
"pending": "Pendente",
"approved": "Aprovado",
"available": "Disponível",
"processing": "Processing"
"processing": "Processando"
},
"sabnzbd": {
"rate": "Taxa",
@@ -192,9 +192,9 @@
"transferRate": "Taxa"
},
"authentik": {
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)",
"users": "Users"
"loginsLast24H": "Inícios de sessão (24h)",
"failedLoginsLast24H": "Inícios de sessão falhados (24h)",
"users": "Utilizadores"
},
"proxmox": {
"mem": "MEM",
@@ -204,13 +204,13 @@
},
"unifi": {
"users": "Utilizadores",
"uptime": "Tempo de Atividade do Sistema",
"uptime": "Sistema Ativo",
"days": "Dias",
"wan": "WAN",
"lan_users": "Utilizadores LAN",
"wlan_users": "Utilizadores WLAN",
"up": "Ligados",
"down": "Desligados",
"up": "Ligado",
"down": "Desligado",
"wait": "Por favor, aguarde",
"lan": "LAN",
"wlan": "WLAN",
@@ -241,7 +241,7 @@
"2-night": "Parcialmente nublado",
"3-day": "Nublado",
"3-night": "Nublado",
"99-night": "Thunderstorm With Hail",
"99-night": "Trovoada com granizo",
"45-day": "Nevoeiro",
"45-night": "Nevoeiro",
"48-day": "Nevoeiro",
@@ -257,160 +257,160 @@
"57-day": "Freezing Drizzle",
"57-night": "Freezing Drizzle",
"66-day": "Freezing Rain",
"61-day": "Light Rain",
"61-night": "Light Rain",
"63-day": "Rain",
"63-night": "Rain",
"65-day": "Heavy Rain",
"61-day": "Chuva fraca",
"61-night": "Chuva fraca",
"63-day": "Chuva",
"63-night": "Chuva",
"65-day": "Chuva forte",
"66-night": "Freezing Rain",
"65-night": "Heavy Rain",
"65-night": "Chuva forte",
"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",
"71-day": "Neve fraca",
"71-night": "Neve fraca",
"73-day": "Neve",
"73-night": "Neve",
"75-day": "Neve forte",
"75-night": "Neve forte",
"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",
"80-day": "Neve fraca",
"80-night": "Chuviscos ligeiros",
"81-day": "Chuviscos",
"81-night": "Chuviscos",
"82-day": "Chuviscos fortes",
"82-night": "Chuviscos fortes",
"85-day": "Snow Showers",
"85-night": "Snow Showers",
"86-day": "Snow Showers",
"86-night": "Snow Showers",
"95-day": "Thunderstorm",
"95-night": "Thunderstorm",
"96-day": "Thunderstorm With Hail",
"96-night": "Thunderstorm With Hail",
"99-day": "Thunderstorm With Hail"
"95-day": "Trovoada",
"95-night": "Trovoada",
"96-day": "Trovoada com granizo",
"96-night": "Trovoada com granizo",
"99-day": "Trovoada com granizo"
},
"quicklaunch": {
"bookmark": "Marcador",
"service": "Serviço"
},
"homebridge": {
"available_update": "System",
"updates": "Updates",
"update_available": "Update Available",
"up_to_date": "Up to Date",
"available_update": "Sistema",
"updates": "Atualizações",
"update_available": "Atualização disponível",
"up_to_date": "Atualizado",
"child_bridges": "Child Bridges",
"child_bridges_status": "{{ok}}/{{total}}"
},
"autobrr": {
"approvedPushes": "Approved",
"rejectedPushes": "Rejected",
"filters": "Filters",
"indexers": "Indexers"
"approvedPushes": "Aprovado",
"rejectedPushes": "Rejeitado",
"filters": "Filtros",
"indexers": "Indexadores"
},
"watchtower": {
"containers_scanned": "Scanned",
"containers_updated": "Updated",
"containers_failed": "Failed"
"containers_scanned": "Verificado",
"containers_updated": "Atualizado",
"containers_failed": "Falhou"
},
"tubearchivist": {
"downloads": "Queue",
"videos": "Videos",
"channels": "Channels",
"playlists": "Playlists"
"downloads": "Fila",
"videos": "Vídeos",
"channels": "Canais",
"playlists": "Listas"
},
"truenas": {
"load": "System Load",
"uptime": "Uptime",
"alerts": "Alerts",
"load": "Carga do sistema",
"uptime": "Ligado",
"alerts": "Alertas",
"time": "{{value, number(style: unit; unitDisplay: long;)}}"
},
"navidrome": {
"nothing_streaming": "No Active Streams",
"please_wait": "Please Wait"
"nothing_streaming": "Sem streams ativos",
"please_wait": "Por favor aguarde"
},
"pyload": {
"queue": "Queue",
"queue": "Fila",
"total": "Total",
"speed": "Speed",
"active": "Active"
"speed": "Velocidade",
"active": "Ativo"
},
"gluetun": {
"region": "Region",
"country": "Country",
"public_ip": "Public IP"
"region": "Região",
"country": "País",
"public_ip": "IP público"
},
"hdhomerun": {
"channels": "Channels",
"channels": "Canais",
"hd": "HD"
},
"ping": {
"error": "Erro",
"ping": "Ping"
"ping": "Tempo de resposta"
},
"scrutiny": {
"passed": "Passed",
"failed": "Failed",
"unknown": "Unknown"
"passed": "Aprovado",
"failed": "Falhou",
"unknown": "Desconhecido"
},
"paperlessngx": {
"inbox": "Inbox",
"inbox": "Caixa de entrada",
"total": "Total"
},
"deluge": {
"download": "Download",
"upload": "Upload",
"download": "Descarregar",
"upload": "Enviar",
"leech": "Leech",
"seed": "Seed"
"seed": "Semente"
},
"flood": {
"download": "Descarregar",
"upload": "Carregar",
"leech": "Leech",
"seed": "Seed"
"seed": "Semente"
},
"tdarr": {
"queue": "Queue",
"processed": "Processed",
"errored": "Errored",
"saved": "Saved"
"queue": "Fila",
"processed": "Processado",
"errored": "Erro",
"saved": "Guardado"
},
"miniflux": {
"read": "Read",
"unread": "Unread"
"read": "Lido",
"unread": "Não lido"
},
"nextdns": {
"wait": "Please Wait",
"no_devices": "No Device Data Received"
"wait": "Aguarde",
"no_devices": "Nenhum dado do dispositivo recebido"
},
"omada": {
"connectedAp": "Connected APs",
"activeUser": "Active devices",
"alerts": "Alerts",
"connectedGateway": "Connected gateways",
"connectedSwitches": "Connected switches"
"connectedAp": "APs Ligados",
"activeUser": "Dispositivos activos",
"alerts": "Alertas",
"connectedGateway": "Gateways ligados",
"connectedSwitches": "Switches ligados"
},
"downloadstation": {
"download": "Download",
"upload": "Upload",
"download": "Descarregar",
"upload": "Enviar",
"leech": "Leech",
"seed": "Seed"
"seed": "Semente"
},
"mikrotik": {
"cpuLoad": "CPU Load",
"memoryUsed": "Memory Used",
"uptime": "Uptime",
"cpuLoad": "Carga do CPU",
"memoryUsed": "Memória Utilizada",
"uptime": "Ativo",
"numberOfLeases": "Leases"
},
"xteve": {
"streams_all": "All Streams",
"streams_active": "Active Streams",
"streams_xepg": "XEPG Channels"
"streams_all": "Todos os Streams",
"streams_active": "Streams ativos",
"streams_xepg": "Canais XEPG"
},
"opnsense": {
"cpu": "CPU Load",
"memory": "Active Memory",
"wanUpload": "WAN Upload",
"wanDownload": "WAN Download"
"cpu": "Carga do CPU",
"memory": "Memória Ativa",
"wanUpload": "Envio WAN",
"wanDownload": "WAN Descarga"
}
}

View File

@@ -3,8 +3,6 @@ import useSWR from "swr";
import { compareVersions } from "compare-versions";
import { MdNewReleases } from "react-icons/md";
import cachedFetch from "utils/proxy/cached-fetch";
export default function Version() {
const { t, i18n } = useTranslation();
@@ -12,9 +10,7 @@ export default function Version() {
const revision = process.env.NEXT_PUBLIC_REVISION?.length ? process.env.NEXT_PUBLIC_REVISION : "dev";
const version = process.env.NEXT_PUBLIC_VERSION?.length ? process.env.NEXT_PUBLIC_VERSION : "dev";
const cachedFetcher = (resource) => cachedFetch(resource, 5).then((res) => res.json());
const { data: releaseData } = useSWR("https://api.github.com/repos/benphelps/homepage/releases", cachedFetcher);
const { data: releaseData } = useSWR("/api/releases");
// use Intl.DateTimeFormat to format the date
const formatDate = (date) => {
@@ -48,7 +44,7 @@ export default function Version() {
</span>
{version === "main" || version === "dev" || version === "nightly"
? null
: releaseData &&
: releaseData && latestRelease &&
compareVersions(latestRelease.tag_name, version) > 0 && (
<a
href={latestRelease.html_url}

View File

@@ -13,22 +13,23 @@ const textSizes = {
};
export default function DateTime({ options }) {
const { text_size: textSize, format } = options;
const { text_size: textSize, locale, format } = options;
const { i18n } = useTranslation();
const [date, setDate] = useState("");
const dateLocale = locale ?? i18n.language;
useEffect(() => {
const dateFormat = new Intl.DateTimeFormat(i18n.language, { ...format });
const dateFormat = new Intl.DateTimeFormat(dateLocale, { ...format });
const interval = setInterval(() => {
setDate(dateFormat.format(new Date()));
}, 1000);
return () => clearInterval(interval);
}, [date, setDate, i18n.language, format]);
}, [date, setDate, dateLocale, format]);
return (
<div className="flex flex-col justify-center first:ml-0 ml-4">
<div className="flex flex-row items-center grow justify-end">
<span className={`text-theme-800 dark:text-theme-200 ${textSizes[textSize || "lg"]}`}>
<span className={`text-theme-800 dark:text-theme-200 tabular-nums ${textSizes[textSize || "lg"]}`}>
{date}
</span>
</div>

View File

@@ -38,7 +38,7 @@ export default function Cpu({ expanded }) {
<div className="pr-1">{t("resources.load")}</div>
</div>
)}
<UsageBar percent={100} />
<UsageBar percent={0} />
</div>
</div>
);

View File

@@ -38,7 +38,7 @@ export default function Disk({ options, expanded }) {
<div className="pr-1">{t("resources.total")}</div>
</span>
)}
<UsageBar percent={100} />
<UsageBar percent={0} />
</div>
</div>
);

View File

@@ -38,7 +38,7 @@ export default function Memory({ expanded }) {
<div className="pr-1">{t("resources.total")}</div>
</span>
)}
<UsageBar percent={100} />
<UsageBar percent={0} />
</div>
</div>
);

View File

@@ -0,0 +1,6 @@
import cachedFetch from "utils/proxy/cached-fetch";
export default async function handler(req, res) {
const releasesURL = "https://api.github.com/repos/benphelps/homepage/releases";
return res.send(await cachedFetch(releasesURL, 5));
}

View File

@@ -50,9 +50,12 @@ export async function servicesResponse() {
try {
discoveredServices = cleanServiceGroups(await servicesFromDocker());
if (discoveredServices?.length === 0) {
console.debug("No containers were found with homepage labels.");
}
} catch (e) {
console.error("Failed to discover services, please check docker.yaml for errors or remove example entries.");
if (e) console.error(e);
if (e) console.error(e.toString());
discoveredServices = [];
}
@@ -60,7 +63,7 @@ export async function servicesResponse() {
configuredServices = cleanServiceGroups(await servicesFromConfig());
} catch (e) {
console.error("Failed to load services.yaml, please check for errors");
if (e) console.error(e);
if (e) console.error(e.toString());
configuredServices = [];
}
@@ -68,7 +71,7 @@ export async function servicesResponse() {
initialSettings = await getSettings();
} catch (e) {
console.error("Failed to load settings.yaml, please check for errors");
if (e) console.error(e);
if (e) console.error(e.toString());
initialSettings = {};
}

View File

@@ -44,36 +44,41 @@ export async function servicesFromDocker() {
const serviceServers = await Promise.all(
Object.keys(servers).map(async (serverName) => {
const docker = new Docker(getDockerArguments(serverName).conn);
const containers = await docker.listContainers({
all: true,
});
// bad docker connections can result in a <Buffer ...> object?
// in any case, this ensures the result is the expected array
if (!Array.isArray(containers)) {
return [];
}
const discovered = containers.map((container) => {
let constructedService = null;
Object.keys(container.Labels).forEach((label) => {
if (label.startsWith("homepage.")) {
if (!constructedService) {
constructedService = {
container: container.Names[0].replace(/^\//, ""),
server: serverName,
};
}
shvl.set(constructedService, label.replace("homepage.", ""), container.Labels[label]);
}
try {
const docker = new Docker(getDockerArguments(serverName).conn);
const containers = await docker.listContainers({
all: true,
});
return constructedService;
});
// bad docker connections can result in a <Buffer ...> object?
// in any case, this ensures the result is the expected array
if (!Array.isArray(containers)) {
return [];
}
return { server: serverName, services: discovered.filter((filteredService) => filteredService) };
const discovered = containers.map((container) => {
let constructedService = null;
Object.keys(container.Labels).forEach((label) => {
if (label.startsWith("homepage.")) {
if (!constructedService) {
constructedService = {
container: container.Names[0].replace(/^\//, ""),
server: serverName,
};
}
shvl.set(constructedService, label.replace("homepage.", ""), container.Labels[label]);
}
});
return constructedService;
});
return { server: serverName, services: discovered.filter((filteredService) => filteredService) };
} catch (e) {
// a server failed, but others may succeed
return { server: serverName, services: [] };
}
})
);

View File

@@ -30,6 +30,8 @@ export default async function credentialedProxyHandler(req, res, map) {
headers["X-gotify-Key"] = `${widget.key}`;
} else if (widget.type === "authentik") {
headers.Authorization = `Bearer ${widget.key}`;
} else if (widget.type === "truenas") {
headers.Authorization = `Bearer ${widget.key}`;
} else if (widget.type === "proxmox") {
headers.Authorization = `PVEAPIToken=${widget.username}=${widget.password}`;
} else if (widget.type === "autobrr") {

View File

@@ -5,10 +5,8 @@ import widgets from "widgets/widgets";
import getServiceWidget from "utils/config/service-helpers";
const logger = createLogger("downloadstationProxyHandler");
const authApi = "{url}/webapi/auth.cgi?api=SYNO.API.Auth&version=2&method=login&account={username}&passwd={password}&session=DownloadStation&format=cookie"
async function login(widget) {
const loginUrl = formatApiCall(authApi, widget);
async function login(loginUrl) {
const [status, contentType, data] = await httpProxy(loginUrl);
if (status !== 200) {
return [status, contentType, data];
@@ -56,8 +54,28 @@ export default async function downloadstationProxyHandler(req, res) {
const json = JSON.parse(data.toString());
if (json?.success !== true) {
logger.debug("Logging in to DownloadStation");
[status, contentType, data] = await login(widget);
logger.debug("Attempting login to DownloadStation");
const apiInfoUrl = formatApiCall("{url}/webapi/query.cgi?api=SYNO.API.Info&version=1&method=query", widget);
let path = "entry.cgi";
let maxVersion = 7;
[status, contentType, data] = await httpProxy(apiInfoUrl);
if (status === 200) {
try {
const apiAuthInfo = JSON.parse(data.toString()).data['SYNO.API.Auth'];
if (apiAuthInfo) {
path = apiAuthInfo.path;
maxVersion = apiAuthInfo.maxVersion;
logger.debug(`Deteceted Downloadstation auth API path: ${path} and maxVersion: ${maxVersion}`);
}
} catch {
logger.debug(`Error ${status} obtaining DownloadStation API info`);
}
}
const authApi = `{url}/webapi/${path}?api=SYNO.API.Auth&version=${maxVersion}&method=login&account={username}&passwd={password}&session=DownloadStation&format=cookie`
const loginUrl = formatApiCall(authApi, widget);
[status, contentType, data] = await login(loginUrl);
if (status !== 200) {
return res.status(status).end(data)
}

View File

@@ -42,9 +42,9 @@ export default async function omadaProxyHandler(req, res) {
if (widget) {
const {url} = widget;
const { url } = widget;
const controllerInfoURL = `${widget.url}/api/info`;
const controllerInfoURL = `${url}/api/info`;
let [status, contentType, data] = await httpProxy(controllerInfoURL, {
headers: {
@@ -77,13 +77,13 @@ export default async function omadaProxyHandler(req, res) {
switch (controllerVersionMajor) {
case 3:
loginUrl = `${widget.url}/api/user/login?ajax`;
loginUrl = `${url}/api/user/login?ajax`;
break;
case 4:
loginUrl = `${widget.url}/api/v2/login`;
loginUrl = `${url}/api/v2/login`;
break;
case 5:
loginUrl = `${widget.url}/${cId}/api/v2/login`;
loginUrl = `${url}/${cId}/api/v2/login`;
break;
default:
break;
@@ -105,7 +105,7 @@ export default async function omadaProxyHandler(req, res) {
switch (controllerVersionMajor) {
case 3:
sitesUrl = `${widget.url}/web/v1/controller?ajax=&token=${token}`;
sitesUrl = `${url}/web/v1/controller?ajax=&token=${token}`;
body = {
"method": "getUserSites",
"params": {
@@ -115,10 +115,10 @@ export default async function omadaProxyHandler(req, res) {
method = "POST";
break;
case 4:
sitesUrl = `${widget.url}/api/v2/sites?token=${token}&currentPage=1&currentPageSize=1000`;
sitesUrl = `${url}/api/v2/sites?token=${token}&currentPage=1&currentPageSize=1000`;
break;
case 5:
sitesUrl = `${widget.url}/${cId}/api/v2/sites?token=${token}&currentPage=1&currentPageSize=1000`;
sitesUrl = `${url}/${cId}/api/v2/sites?token=${token}&currentPage=1&currentPageSize=1000`;
break;
default:
break;
@@ -133,7 +133,7 @@ export default async function omadaProxyHandler(req, res) {
const sitesResponseData = JSON.parse(data);
if (sitesResponseData.errorCode > 0) {
if (status !== 200 || sitesResponseData.errorCode > 0) {
logger.debug(`HTTTP ${status} getting sites list: ${sitesResponseData.msg}`);
return res.status(status).json({error: {message: "Error getting sites list", url, data: sitesResponseData}});
}
@@ -143,7 +143,7 @@ export default async function omadaProxyHandler(req, res) {
sitesResponseData.result.data.find(s => s.name === widget.site);
if (!site) {
return res.status(status).json({error: {message: `Site ${widget.site} is not found`, url, data}});
return res.status(status).json({error: {message: `Site ${widget.site} is not found`, url: sitesUrl, data}});
}
let siteResponseData;
@@ -156,7 +156,7 @@ export default async function omadaProxyHandler(req, res) {
if (controllerVersionMajor === 3) {
// Omada v3 controller requires switching site
const switchUrl = `${widget.url}/web/v1/controller?ajax=&token=${token}`;
const switchUrl = `${url}/web/v1/controller?ajax=&token=${token}`;
method = "POST";
body = {
method: "switchSite",
@@ -181,7 +181,7 @@ export default async function omadaProxyHandler(req, res) {
return res.status(status).json({error: {message: "Error switching site", url: switchUrl, data}});
}
const statsUrl = `${widget.url}/web/v1/controller?getGlobalStat=&token=${token}`;
const statsUrl = `${url}/web/v1/controller?getGlobalStat=&token=${token}`;
[status, contentType, data] = await httpProxy(statsUrl, {
method,
params,

View File

@@ -84,9 +84,9 @@ export default async function pyloadProxyHandler(req, res) {
if (data?.error || status !== 200) {
try {
return res.status(status).send({error: {message: "HTTP error communicating with Plex API", data: Buffer.from(data).toString()}});
return res.status(status).send({error: {message: "HTTP error communicating with Pyload API", data: Buffer.from(data).toString()}});
} catch (e) {
return res.status(status).send({error: {message: "HTTP error communicating with Plex API", data}});
return res.status(status).send({error: {message: "HTTP error communicating with Pyload API", data}});
}
}
@@ -95,7 +95,7 @@ export default async function pyloadProxyHandler(req, res) {
}
} catch (e) {
logger.error(e);
return res.status(500).send({error: {message: `Error communicating with Plex API: ${e.toString()}`}});
return res.status(500).send({error: {message: `Error communicating with Pyload API: ${e.toString()}`}});
}
return res.status(400).json({ error: 'Invalid proxy service type' });

View File

@@ -1,9 +1,31 @@
import { jsonArrayFilter } from "utils/proxy/api-helpers";
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
import genericProxyHandler from "utils/proxy/handlers/generic";
import getServiceWidget from "utils/config/service-helpers";
const widget = {
api: "{url}/api/v2.0/{endpoint}",
proxyHandler: genericProxyHandler,
proxyHandler: async (req, res, map) => { // choose proxy handler based on widget settings
const { group, service } = req.query;
if (group && service) {
const widgetOpts = await getServiceWidget(group, service);
let handler;
if (widgetOpts.username && widgetOpts.password) {
handler = genericProxyHandler;
} else if (widgetOpts.key) {
handler = credentialedProxyHandler;
}
if (handler) {
return handler(req, res, map);
}
return res.status(500).json({ error: "Username / password or API key required" });
}
return res.status(500).json({ error: "Error parsing widget request" });
},
mappings: {
alerts: {