mirror of
https://github.com/gethomepage/homepage.git
synced 2026-01-05 23:12:08 +08:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1799f17e3 | ||
|
|
730f1c5ec1 | ||
|
|
34a7b25c9c | ||
|
|
f93106970a | ||
|
|
2b4fb03dcf | ||
|
|
11fad11b6d | ||
|
|
885dca1750 | ||
|
|
1caa9faded | ||
|
|
4f24c0f909 | ||
|
|
1fd198c255 | ||
|
|
84b7f103c3 | ||
|
|
272be2c086 | ||
|
|
6705197a35 | ||
|
|
2ac06937f9 | ||
|
|
35a2cd9b94 | ||
|
|
3a43cf247b | ||
|
|
af29f5b266 | ||
|
|
f6b1304e22 | ||
|
|
ee729a7e6a | ||
|
|
bc7937db71 | ||
|
|
0e1aeaf54c | ||
|
|
2e8717247d | ||
|
|
d17a17bd3c | ||
|
|
0afc1b96f1 | ||
|
|
5fbc6702bc | ||
|
|
75455a23e2 | ||
|
|
2aed46671f | ||
|
|
88934ec39a | ||
|
|
21c0c687cd | ||
|
|
6b90d3ef28 |
16
README.md
16
README.md
@@ -45,15 +45,17 @@
|
|||||||
- Container status (Running / Stopped) & statistics (CPU, Memory, Network)
|
- Container status (Running / Stopped) & statistics (CPU, Memory, Network)
|
||||||
- Automatic service discovery (via labels)
|
- Automatic service discovery (via labels)
|
||||||
- Service Integration
|
- Service Integration
|
||||||
- Sonarr, Radarr, Readarr, Prowlarr, Bazarr, Lidarr, Emby, Jellyfin, Tautulli (Plex)
|
- Sonarr, Radarr, Readarr, Prowlarr, Bazarr, Lidarr, Emby, Jellyfin, Tautulli, Plex and more
|
||||||
- Ombi, Overseerr, Jellyseerr, Jackett, NZBGet, SABnzbd, ruTorrent, Transmission, qBittorrent
|
- 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
|
- Portainer, Traefik, Speedtest Tracker, PiHole, AdGuard Home, Nginx Proxy Manager, Gotify, Syncthing Relay Server, Authentik, Proxmox and more
|
||||||
- Information Providers
|
- Information Providers
|
||||||
- Coin Market Cap, Mastodon
|
- Coin Market Cap, Mastodon and more
|
||||||
- Information & Utility Widgets
|
- Information & Utility Widgets
|
||||||
- System Stats (Disk, CPU, Memory)
|
- System Stats (Disk, CPU, Memory)
|
||||||
- Weather via [OpenWeatherMap](https://openweathermap.org/) or [Open-Meteo](https://open-meteo.com/)
|
- 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
|
- Customizable
|
||||||
- 21 theme colors with light and dark mode support
|
- 21 theme colors with light and dark mode support
|
||||||
- Background image 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 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
|
## Getting Started
|
||||||
|
|
||||||
@@ -83,7 +85,7 @@ services:
|
|||||||
- 3000:3000
|
- 3000:3000
|
||||||
volumes:
|
volumes:
|
||||||
- /path/to/config:/app/config # Make sure your local config directory exists
|
- /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:
|
or docker run:
|
||||||
|
|||||||
@@ -394,14 +394,14 @@
|
|||||||
"numberOfLeases": "Alquileres"
|
"numberOfLeases": "Alquileres"
|
||||||
},
|
},
|
||||||
"xteve": {
|
"xteve": {
|
||||||
"streams_all": "All Streams",
|
"streams_all": "Todas las corrientes",
|
||||||
"streams_active": "Active Streams",
|
"streams_active": "Corrientes activas",
|
||||||
"streams_xepg": "XEPG Channels"
|
"streams_xepg": "Canales XEPG"
|
||||||
},
|
},
|
||||||
"opnsense": {
|
"opnsense": {
|
||||||
"cpu": "CPU Load",
|
"cpu": "Carga de la CPU",
|
||||||
"memory": "Active Memory",
|
"memory": "Memoria activa",
|
||||||
"wanUpload": "WAN Upload",
|
"wanUpload": "Carga WAN",
|
||||||
"wanDownload": "WAN Download"
|
"wanDownload": "Descargar WAN"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -399,9 +399,9 @@
|
|||||||
"streams_xepg": "Canal XEPG"
|
"streams_xepg": "Canal XEPG"
|
||||||
},
|
},
|
||||||
"opnsense": {
|
"opnsense": {
|
||||||
"cpu": "CPU Load",
|
"cpu": "Charge CPU",
|
||||||
"memory": "Active Memory",
|
"memory": "Mém. Utilisée",
|
||||||
"wanUpload": "WAN Upload",
|
"wanUpload": "WAN Envoi",
|
||||||
"wanDownload": "WAN Download"
|
"wanDownload": "WAN Récep."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -347,61 +347,61 @@
|
|||||||
"deluge": {
|
"deluge": {
|
||||||
"download": "Preuzimanje",
|
"download": "Preuzimanje",
|
||||||
"upload": "Prijenos",
|
"upload": "Prijenos",
|
||||||
"leech": "Leech",
|
"leech": "Korištenje tuđeg sadržaja",
|
||||||
"seed": "Seed"
|
"seed": "Prenošenje preuzetog sadržaja"
|
||||||
},
|
},
|
||||||
"flood": {
|
"flood": {
|
||||||
"download": "Preuzimanje",
|
"download": "Preuzimanje",
|
||||||
"upload": "Prijenos",
|
"upload": "Prijenos",
|
||||||
"leech": "Leech",
|
"leech": "Korištenje tuđeg sadržaja",
|
||||||
"seed": "Seed"
|
"seed": "Prenošenje preuzetog sadržaja"
|
||||||
},
|
},
|
||||||
"tdarr": {
|
"tdarr": {
|
||||||
"queue": "Queue",
|
"queue": "Red čekanja",
|
||||||
"processed": "Processed",
|
"processed": "Obrađeno",
|
||||||
"errored": "Errored",
|
"errored": "S greškom",
|
||||||
"saved": "Saved"
|
"saved": "Spremljeno"
|
||||||
},
|
},
|
||||||
"miniflux": {
|
"miniflux": {
|
||||||
"read": "Read",
|
"read": "Pročitano",
|
||||||
"unread": "Unread"
|
"unread": "Nepročitano"
|
||||||
},
|
},
|
||||||
"nextdns": {
|
"nextdns": {
|
||||||
"wait": "Please Wait",
|
"wait": "Pričekaj",
|
||||||
"no_devices": "No Device Data Received"
|
"no_devices": "Podaci o uređaju nisu primljeni"
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
"bibyterate": "{{value, rate(bits: false; binary: true)}}",
|
||||||
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
"bibitrate": "{{value, rate(bits: true; binary: true)}}"
|
||||||
},
|
},
|
||||||
"omada": {
|
"omada": {
|
||||||
"connectedAp": "Connected APs",
|
"connectedAp": "Povezani AP-ovi",
|
||||||
"activeUser": "Active devices",
|
"activeUser": "Aktivni uređaji",
|
||||||
"alerts": "Alerts",
|
"alerts": "Upozorenja",
|
||||||
"connectedGateway": "Connected gateways",
|
"connectedGateway": "Povezani pristupi",
|
||||||
"connectedSwitches": "Connected switches"
|
"connectedSwitches": "Povezani prekidači"
|
||||||
},
|
},
|
||||||
"downloadstation": {
|
"downloadstation": {
|
||||||
"download": "Download",
|
"download": "Preuzimanje",
|
||||||
"upload": "Upload",
|
"upload": "Prijenos",
|
||||||
"leech": "Leech",
|
"leech": "Korištenje tuđeg sadržaja",
|
||||||
"seed": "Seed"
|
"seed": "Prenošenje preuzetog sadržaja"
|
||||||
},
|
},
|
||||||
"mikrotik": {
|
"mikrotik": {
|
||||||
"cpuLoad": "CPU Load",
|
"cpuLoad": "CPU Load",
|
||||||
"memoryUsed": "Memory Used",
|
"memoryUsed": "Korištena memorija",
|
||||||
"uptime": "Uptime",
|
"uptime": "Uptime",
|
||||||
"numberOfLeases": "Leases"
|
"numberOfLeases": "Leases"
|
||||||
},
|
},
|
||||||
"xteve": {
|
"xteve": {
|
||||||
"streams_all": "All Streams",
|
"streams_all": "Svi prijenosi",
|
||||||
"streams_active": "Active Streams",
|
"streams_active": "Aktivni prijenosi",
|
||||||
"streams_xepg": "XEPG Channels"
|
"streams_xepg": "XEPG kanali"
|
||||||
},
|
},
|
||||||
"opnsense": {
|
"opnsense": {
|
||||||
"cpu": "CPU Load",
|
"cpu": "CPU Load",
|
||||||
"memory": "Active Memory",
|
"memory": "Aktivna memorija",
|
||||||
"wanUpload": "WAN Upload",
|
"wanUpload": "WAN prijenos",
|
||||||
"wanDownload": "WAN Download"
|
"wanDownload": "WAN preuzimanje"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
407
public/locales/ja/common.json
Normal file
407
public/locales/ja/common.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"widget": {
|
"widget": {
|
||||||
"missing_type": "Widget ausente: {{type}}",
|
"missing_type": "Tipo de Widget ausente: {{type}}",
|
||||||
"api_error": "Erro da API",
|
"api_error": "Erro da API",
|
||||||
"status": "Estado",
|
"status": "Estado",
|
||||||
"information": "Informação",
|
"information": "Informação",
|
||||||
@@ -14,8 +14,8 @@
|
|||||||
"resources": {
|
"resources": {
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"free": "Livre",
|
"free": "Livre",
|
||||||
"used": "Usado",
|
"used": "Utilizado",
|
||||||
"load": "Carregar",
|
"load": "Carga",
|
||||||
"cpu": "CPU"
|
"cpu": "CPU"
|
||||||
},
|
},
|
||||||
"docker": {
|
"docker": {
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
"wanted": "Desejado",
|
"wanted": "Desejado",
|
||||||
"queued": "Fila",
|
"queued": "Fila",
|
||||||
"movies": "Filmes",
|
"movies": "Filmes",
|
||||||
"missing": "Missing"
|
"missing": "Faltando"
|
||||||
},
|
},
|
||||||
"readarr": {
|
"readarr": {
|
||||||
"wanted": "Desejados",
|
"wanted": "Desejados",
|
||||||
@@ -111,14 +111,14 @@
|
|||||||
"weather": {
|
"weather": {
|
||||||
"current": "Localização atual",
|
"current": "Localização atual",
|
||||||
"allow": "Clicar para permitir",
|
"allow": "Clicar para permitir",
|
||||||
"updating": "A atualizar",
|
"updating": "Atualizando",
|
||||||
"wait": "Por favor aguarde"
|
"wait": "Por favor aguarde"
|
||||||
},
|
},
|
||||||
"overseerr": {
|
"overseerr": {
|
||||||
"pending": "Pendente",
|
"pending": "Pendente",
|
||||||
"approved": "Aprovado",
|
"approved": "Aprovado",
|
||||||
"available": "Disponível",
|
"available": "Disponível",
|
||||||
"processing": "Processing"
|
"processing": "Processando"
|
||||||
},
|
},
|
||||||
"sabnzbd": {
|
"sabnzbd": {
|
||||||
"rate": "Taxa",
|
"rate": "Taxa",
|
||||||
@@ -192,9 +192,9 @@
|
|||||||
"transferRate": "Taxa"
|
"transferRate": "Taxa"
|
||||||
},
|
},
|
||||||
"authentik": {
|
"authentik": {
|
||||||
"loginsLast24H": "Logins (24h)",
|
"loginsLast24H": "Inícios de sessão (24h)",
|
||||||
"failedLoginsLast24H": "Failed Logins (24h)",
|
"failedLoginsLast24H": "Inícios de sessão falhados (24h)",
|
||||||
"users": "Users"
|
"users": "Utilizadores"
|
||||||
},
|
},
|
||||||
"proxmox": {
|
"proxmox": {
|
||||||
"mem": "MEM",
|
"mem": "MEM",
|
||||||
@@ -204,13 +204,13 @@
|
|||||||
},
|
},
|
||||||
"unifi": {
|
"unifi": {
|
||||||
"users": "Utilizadores",
|
"users": "Utilizadores",
|
||||||
"uptime": "Tempo de Atividade do Sistema",
|
"uptime": "Sistema Ativo",
|
||||||
"days": "Dias",
|
"days": "Dias",
|
||||||
"wan": "WAN",
|
"wan": "WAN",
|
||||||
"lan_users": "Utilizadores LAN",
|
"lan_users": "Utilizadores LAN",
|
||||||
"wlan_users": "Utilizadores WLAN",
|
"wlan_users": "Utilizadores WLAN",
|
||||||
"up": "Ligados",
|
"up": "Ligado",
|
||||||
"down": "Desligados",
|
"down": "Desligado",
|
||||||
"wait": "Por favor, aguarde",
|
"wait": "Por favor, aguarde",
|
||||||
"lan": "LAN",
|
"lan": "LAN",
|
||||||
"wlan": "WLAN",
|
"wlan": "WLAN",
|
||||||
@@ -241,7 +241,7 @@
|
|||||||
"2-night": "Parcialmente nublado",
|
"2-night": "Parcialmente nublado",
|
||||||
"3-day": "Nublado",
|
"3-day": "Nublado",
|
||||||
"3-night": "Nublado",
|
"3-night": "Nublado",
|
||||||
"99-night": "Thunderstorm With Hail",
|
"99-night": "Trovoada com granizo",
|
||||||
"45-day": "Nevoeiro",
|
"45-day": "Nevoeiro",
|
||||||
"45-night": "Nevoeiro",
|
"45-night": "Nevoeiro",
|
||||||
"48-day": "Nevoeiro",
|
"48-day": "Nevoeiro",
|
||||||
@@ -257,160 +257,160 @@
|
|||||||
"57-day": "Freezing Drizzle",
|
"57-day": "Freezing Drizzle",
|
||||||
"57-night": "Freezing Drizzle",
|
"57-night": "Freezing Drizzle",
|
||||||
"66-day": "Freezing Rain",
|
"66-day": "Freezing Rain",
|
||||||
"61-day": "Light Rain",
|
"61-day": "Chuva fraca",
|
||||||
"61-night": "Light Rain",
|
"61-night": "Chuva fraca",
|
||||||
"63-day": "Rain",
|
"63-day": "Chuva",
|
||||||
"63-night": "Rain",
|
"63-night": "Chuva",
|
||||||
"65-day": "Heavy Rain",
|
"65-day": "Chuva forte",
|
||||||
"66-night": "Freezing Rain",
|
"66-night": "Freezing Rain",
|
||||||
"65-night": "Heavy Rain",
|
"65-night": "Chuva forte",
|
||||||
"67-day": "Freezing Rain",
|
"67-day": "Freezing Rain",
|
||||||
"67-night": "Freezing Rain",
|
"67-night": "Freezing Rain",
|
||||||
"71-day": "Light Snow",
|
"71-day": "Neve fraca",
|
||||||
"71-night": "Light Snow",
|
"71-night": "Neve fraca",
|
||||||
"73-day": "Snow",
|
"73-day": "Neve",
|
||||||
"73-night": "Snow",
|
"73-night": "Neve",
|
||||||
"75-day": "Heavy Snow",
|
"75-day": "Neve forte",
|
||||||
"75-night": "Heavy Snow",
|
"75-night": "Neve forte",
|
||||||
"77-day": "Snow Grains",
|
"77-day": "Snow Grains",
|
||||||
"77-night": "Snow Grains",
|
"77-night": "Snow Grains",
|
||||||
"80-day": "Light Showers",
|
"80-day": "Neve fraca",
|
||||||
"80-night": "Light Showers",
|
"80-night": "Chuviscos ligeiros",
|
||||||
"81-day": "Showers",
|
"81-day": "Chuviscos",
|
||||||
"81-night": "Showers",
|
"81-night": "Chuviscos",
|
||||||
"82-day": "Heavy Showers",
|
"82-day": "Chuviscos fortes",
|
||||||
"82-night": "Heavy Showers",
|
"82-night": "Chuviscos fortes",
|
||||||
"85-day": "Snow Showers",
|
"85-day": "Snow Showers",
|
||||||
"85-night": "Snow Showers",
|
"85-night": "Snow Showers",
|
||||||
"86-day": "Snow Showers",
|
"86-day": "Snow Showers",
|
||||||
"86-night": "Snow Showers",
|
"86-night": "Snow Showers",
|
||||||
"95-day": "Thunderstorm",
|
"95-day": "Trovoada",
|
||||||
"95-night": "Thunderstorm",
|
"95-night": "Trovoada",
|
||||||
"96-day": "Thunderstorm With Hail",
|
"96-day": "Trovoada com granizo",
|
||||||
"96-night": "Thunderstorm With Hail",
|
"96-night": "Trovoada com granizo",
|
||||||
"99-day": "Thunderstorm With Hail"
|
"99-day": "Trovoada com granizo"
|
||||||
},
|
},
|
||||||
"quicklaunch": {
|
"quicklaunch": {
|
||||||
"bookmark": "Marcador",
|
"bookmark": "Marcador",
|
||||||
"service": "Serviço"
|
"service": "Serviço"
|
||||||
},
|
},
|
||||||
"homebridge": {
|
"homebridge": {
|
||||||
"available_update": "System",
|
"available_update": "Sistema",
|
||||||
"updates": "Updates",
|
"updates": "Atualizações",
|
||||||
"update_available": "Update Available",
|
"update_available": "Atualização disponível",
|
||||||
"up_to_date": "Up to Date",
|
"up_to_date": "Atualizado",
|
||||||
"child_bridges": "Child Bridges",
|
"child_bridges": "Child Bridges",
|
||||||
"child_bridges_status": "{{ok}}/{{total}}"
|
"child_bridges_status": "{{ok}}/{{total}}"
|
||||||
},
|
},
|
||||||
"autobrr": {
|
"autobrr": {
|
||||||
"approvedPushes": "Approved",
|
"approvedPushes": "Aprovado",
|
||||||
"rejectedPushes": "Rejected",
|
"rejectedPushes": "Rejeitado",
|
||||||
"filters": "Filters",
|
"filters": "Filtros",
|
||||||
"indexers": "Indexers"
|
"indexers": "Indexadores"
|
||||||
},
|
},
|
||||||
"watchtower": {
|
"watchtower": {
|
||||||
"containers_scanned": "Scanned",
|
"containers_scanned": "Verificado",
|
||||||
"containers_updated": "Updated",
|
"containers_updated": "Atualizado",
|
||||||
"containers_failed": "Failed"
|
"containers_failed": "Falhou"
|
||||||
},
|
},
|
||||||
"tubearchivist": {
|
"tubearchivist": {
|
||||||
"downloads": "Queue",
|
"downloads": "Fila",
|
||||||
"videos": "Videos",
|
"videos": "Vídeos",
|
||||||
"channels": "Channels",
|
"channels": "Canais",
|
||||||
"playlists": "Playlists"
|
"playlists": "Listas"
|
||||||
},
|
},
|
||||||
"truenas": {
|
"truenas": {
|
||||||
"load": "System Load",
|
"load": "Carga do sistema",
|
||||||
"uptime": "Uptime",
|
"uptime": "Ligado",
|
||||||
"alerts": "Alerts",
|
"alerts": "Alertas",
|
||||||
"time": "{{value, number(style: unit; unitDisplay: long;)}}"
|
"time": "{{value, number(style: unit; unitDisplay: long;)}}"
|
||||||
},
|
},
|
||||||
"navidrome": {
|
"navidrome": {
|
||||||
"nothing_streaming": "No Active Streams",
|
"nothing_streaming": "Sem streams ativos",
|
||||||
"please_wait": "Please Wait"
|
"please_wait": "Por favor aguarde"
|
||||||
},
|
},
|
||||||
"pyload": {
|
"pyload": {
|
||||||
"queue": "Queue",
|
"queue": "Fila",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"speed": "Speed",
|
"speed": "Velocidade",
|
||||||
"active": "Active"
|
"active": "Ativo"
|
||||||
},
|
},
|
||||||
"gluetun": {
|
"gluetun": {
|
||||||
"region": "Region",
|
"region": "Região",
|
||||||
"country": "Country",
|
"country": "País",
|
||||||
"public_ip": "Public IP"
|
"public_ip": "IP público"
|
||||||
},
|
},
|
||||||
"hdhomerun": {
|
"hdhomerun": {
|
||||||
"channels": "Channels",
|
"channels": "Canais",
|
||||||
"hd": "HD"
|
"hd": "HD"
|
||||||
},
|
},
|
||||||
"ping": {
|
"ping": {
|
||||||
"error": "Erro",
|
"error": "Erro",
|
||||||
"ping": "Ping"
|
"ping": "Tempo de resposta"
|
||||||
},
|
},
|
||||||
"scrutiny": {
|
"scrutiny": {
|
||||||
"passed": "Passed",
|
"passed": "Aprovado",
|
||||||
"failed": "Failed",
|
"failed": "Falhou",
|
||||||
"unknown": "Unknown"
|
"unknown": "Desconhecido"
|
||||||
},
|
},
|
||||||
"paperlessngx": {
|
"paperlessngx": {
|
||||||
"inbox": "Inbox",
|
"inbox": "Caixa de entrada",
|
||||||
"total": "Total"
|
"total": "Total"
|
||||||
},
|
},
|
||||||
"deluge": {
|
"deluge": {
|
||||||
"download": "Download",
|
"download": "Descarregar",
|
||||||
"upload": "Upload",
|
"upload": "Enviar",
|
||||||
"leech": "Leech",
|
"leech": "Leech",
|
||||||
"seed": "Seed"
|
"seed": "Semente"
|
||||||
},
|
},
|
||||||
"flood": {
|
"flood": {
|
||||||
"download": "Descarregar",
|
"download": "Descarregar",
|
||||||
"upload": "Carregar",
|
"upload": "Carregar",
|
||||||
"leech": "Leech",
|
"leech": "Leech",
|
||||||
"seed": "Seed"
|
"seed": "Semente"
|
||||||
},
|
},
|
||||||
"tdarr": {
|
"tdarr": {
|
||||||
"queue": "Queue",
|
"queue": "Fila",
|
||||||
"processed": "Processed",
|
"processed": "Processado",
|
||||||
"errored": "Errored",
|
"errored": "Erro",
|
||||||
"saved": "Saved"
|
"saved": "Guardado"
|
||||||
},
|
},
|
||||||
"miniflux": {
|
"miniflux": {
|
||||||
"read": "Read",
|
"read": "Lido",
|
||||||
"unread": "Unread"
|
"unread": "Não lido"
|
||||||
},
|
},
|
||||||
"nextdns": {
|
"nextdns": {
|
||||||
"wait": "Please Wait",
|
"wait": "Aguarde",
|
||||||
"no_devices": "No Device Data Received"
|
"no_devices": "Nenhum dado do dispositivo recebido"
|
||||||
},
|
},
|
||||||
"omada": {
|
"omada": {
|
||||||
"connectedAp": "Connected APs",
|
"connectedAp": "APs Ligados",
|
||||||
"activeUser": "Active devices",
|
"activeUser": "Dispositivos activos",
|
||||||
"alerts": "Alerts",
|
"alerts": "Alertas",
|
||||||
"connectedGateway": "Connected gateways",
|
"connectedGateway": "Gateways ligados",
|
||||||
"connectedSwitches": "Connected switches"
|
"connectedSwitches": "Switches ligados"
|
||||||
},
|
},
|
||||||
"downloadstation": {
|
"downloadstation": {
|
||||||
"download": "Download",
|
"download": "Descarregar",
|
||||||
"upload": "Upload",
|
"upload": "Enviar",
|
||||||
"leech": "Leech",
|
"leech": "Leech",
|
||||||
"seed": "Seed"
|
"seed": "Semente"
|
||||||
},
|
},
|
||||||
"mikrotik": {
|
"mikrotik": {
|
||||||
"cpuLoad": "CPU Load",
|
"cpuLoad": "Carga do CPU",
|
||||||
"memoryUsed": "Memory Used",
|
"memoryUsed": "Memória Utilizada",
|
||||||
"uptime": "Uptime",
|
"uptime": "Ativo",
|
||||||
"numberOfLeases": "Leases"
|
"numberOfLeases": "Leases"
|
||||||
},
|
},
|
||||||
"xteve": {
|
"xteve": {
|
||||||
"streams_all": "All Streams",
|
"streams_all": "Todos os Streams",
|
||||||
"streams_active": "Active Streams",
|
"streams_active": "Streams ativos",
|
||||||
"streams_xepg": "XEPG Channels"
|
"streams_xepg": "Canais XEPG"
|
||||||
},
|
},
|
||||||
"opnsense": {
|
"opnsense": {
|
||||||
"cpu": "CPU Load",
|
"cpu": "Carga do CPU",
|
||||||
"memory": "Active Memory",
|
"memory": "Memória Ativa",
|
||||||
"wanUpload": "WAN Upload",
|
"wanUpload": "Envio WAN",
|
||||||
"wanDownload": "WAN Download"
|
"wanDownload": "WAN Descarga"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ import useSWR from "swr";
|
|||||||
import { compareVersions } from "compare-versions";
|
import { compareVersions } from "compare-versions";
|
||||||
import { MdNewReleases } from "react-icons/md";
|
import { MdNewReleases } from "react-icons/md";
|
||||||
|
|
||||||
import cachedFetch from "utils/proxy/cached-fetch";
|
|
||||||
|
|
||||||
export default function Version() {
|
export default function Version() {
|
||||||
const { t, i18n } = useTranslation();
|
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 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 version = process.env.NEXT_PUBLIC_VERSION?.length ? process.env.NEXT_PUBLIC_VERSION : "dev";
|
||||||
|
|
||||||
const cachedFetcher = (resource) => cachedFetch(resource, 5);
|
const { data: releaseData } = useSWR("/api/releases");
|
||||||
|
|
||||||
const { data: releaseData } = useSWR("https://api.github.com/repos/benphelps/homepage/releases", cachedFetcher);
|
|
||||||
|
|
||||||
// use Intl.DateTimeFormat to format the date
|
// use Intl.DateTimeFormat to format the date
|
||||||
const formatDate = (date) => {
|
const formatDate = (date) => {
|
||||||
@@ -48,7 +44,7 @@ export default function Version() {
|
|||||||
</span>
|
</span>
|
||||||
{version === "main" || version === "dev" || version === "nightly"
|
{version === "main" || version === "dev" || version === "nightly"
|
||||||
? null
|
? null
|
||||||
: releaseData &&
|
: releaseData && latestRelease &&
|
||||||
compareVersions(latestRelease.tag_name, version) > 0 && (
|
compareVersions(latestRelease.tag_name, version) > 0 && (
|
||||||
<a
|
<a
|
||||||
href={latestRelease.html_url}
|
href={latestRelease.html_url}
|
||||||
|
|||||||
@@ -13,22 +13,23 @@ const textSizes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function DateTime({ options }) {
|
export default function DateTime({ options }) {
|
||||||
const { text_size: textSize, format } = options;
|
const { text_size: textSize, locale, format } = options;
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const [date, setDate] = useState("");
|
const [date, setDate] = useState("");
|
||||||
|
const dateLocale = locale ?? i18n.language;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const dateFormat = new Intl.DateTimeFormat(i18n.language, { ...format });
|
const dateFormat = new Intl.DateTimeFormat(dateLocale, { ...format });
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
setDate(dateFormat.format(new Date()));
|
setDate(dateFormat.format(new Date()));
|
||||||
}, 1000);
|
}, 1000);
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [date, setDate, i18n.language, format]);
|
}, [date, setDate, dateLocale, format]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col justify-center first:ml-0 ml-4">
|
<div className="flex flex-col justify-center first:ml-0 ml-4">
|
||||||
<div className="flex flex-row items-center grow justify-end">
|
<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}
|
{date}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default function Cpu({ expanded }) {
|
|||||||
<div className="pr-1">{t("resources.load")}</div>
|
<div className="pr-1">{t("resources.load")}</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<UsageBar percent={100} />
|
<UsageBar percent={0} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default function Disk({ options, expanded }) {
|
|||||||
<div className="pr-1">{t("resources.total")}</div>
|
<div className="pr-1">{t("resources.total")}</div>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<UsageBar percent={100} />
|
<UsageBar percent={0} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default function Memory({ expanded }) {
|
|||||||
<div className="pr-1">{t("resources.total")}</div>
|
<div className="pr-1">{t("resources.total")}</div>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<UsageBar percent={100} />
|
<UsageBar percent={0} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
6
src/pages/api/releases.js
Normal file
6
src/pages/api/releases.js
Normal 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));
|
||||||
|
}
|
||||||
@@ -50,9 +50,12 @@ export async function servicesResponse() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
discoveredServices = cleanServiceGroups(await servicesFromDocker());
|
discoveredServices = cleanServiceGroups(await servicesFromDocker());
|
||||||
|
if (discoveredServices?.length === 0) {
|
||||||
|
console.debug("No containers were found with homepage labels.");
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to discover services, please check docker.yaml for errors or remove example entries.");
|
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 = [];
|
discoveredServices = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +63,7 @@ export async function servicesResponse() {
|
|||||||
configuredServices = cleanServiceGroups(await servicesFromConfig());
|
configuredServices = cleanServiceGroups(await servicesFromConfig());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to load services.yaml, please check for errors");
|
console.error("Failed to load services.yaml, please check for errors");
|
||||||
if (e) console.error(e);
|
if (e) console.error(e.toString());
|
||||||
configuredServices = [];
|
configuredServices = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +71,7 @@ export async function servicesResponse() {
|
|||||||
initialSettings = await getSettings();
|
initialSettings = await getSettings();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to load settings.yaml, please check for errors");
|
console.error("Failed to load settings.yaml, please check for errors");
|
||||||
if (e) console.error(e);
|
if (e) console.error(e.toString());
|
||||||
initialSettings = {};
|
initialSettings = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,36 +44,41 @@ export async function servicesFromDocker() {
|
|||||||
|
|
||||||
const serviceServers = await Promise.all(
|
const serviceServers = await Promise.all(
|
||||||
Object.keys(servers).map(async (serverName) => {
|
Object.keys(servers).map(async (serverName) => {
|
||||||
const docker = new Docker(getDockerArguments(serverName).conn);
|
try {
|
||||||
const containers = await docker.listContainers({
|
const docker = new Docker(getDockerArguments(serverName).conn);
|
||||||
all: true,
|
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]);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
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: [] };
|
||||||
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ export default async function credentialedProxyHandler(req, res, map) {
|
|||||||
headers["X-gotify-Key"] = `${widget.key}`;
|
headers["X-gotify-Key"] = `${widget.key}`;
|
||||||
} else if (widget.type === "authentik") {
|
} else if (widget.type === "authentik") {
|
||||||
headers.Authorization = `Bearer ${widget.key}`;
|
headers.Authorization = `Bearer ${widget.key}`;
|
||||||
|
} else if (widget.type === "truenas") {
|
||||||
|
headers.Authorization = `Bearer ${widget.key}`;
|
||||||
} else if (widget.type === "proxmox") {
|
} else if (widget.type === "proxmox") {
|
||||||
headers.Authorization = `PVEAPIToken=${widget.username}=${widget.password}`;
|
headers.Authorization = `PVEAPIToken=${widget.username}=${widget.password}`;
|
||||||
} else if (widget.type === "autobrr") {
|
} else if (widget.type === "autobrr") {
|
||||||
|
|||||||
@@ -5,10 +5,8 @@ import widgets from "widgets/widgets";
|
|||||||
import getServiceWidget from "utils/config/service-helpers";
|
import getServiceWidget from "utils/config/service-helpers";
|
||||||
|
|
||||||
const logger = createLogger("downloadstationProxyHandler");
|
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) {
|
async function login(loginUrl) {
|
||||||
const loginUrl = formatApiCall(authApi, widget);
|
|
||||||
const [status, contentType, data] = await httpProxy(loginUrl);
|
const [status, contentType, data] = await httpProxy(loginUrl);
|
||||||
if (status !== 200) {
|
if (status !== 200) {
|
||||||
return [status, contentType, data];
|
return [status, contentType, data];
|
||||||
@@ -56,8 +54,28 @@ export default async function downloadstationProxyHandler(req, res) {
|
|||||||
|
|
||||||
const json = JSON.parse(data.toString());
|
const json = JSON.parse(data.toString());
|
||||||
if (json?.success !== true) {
|
if (json?.success !== true) {
|
||||||
logger.debug("Logging in to DownloadStation");
|
logger.debug("Attempting login to DownloadStation");
|
||||||
[status, contentType, data] = await login(widget);
|
|
||||||
|
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) {
|
if (status !== 200) {
|
||||||
return res.status(status).end(data)
|
return res.status(status).end(data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ export default async function omadaProxyHandler(req, res) {
|
|||||||
|
|
||||||
if (widget) {
|
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, {
|
let [status, contentType, data] = await httpProxy(controllerInfoURL, {
|
||||||
headers: {
|
headers: {
|
||||||
@@ -77,13 +77,13 @@ export default async function omadaProxyHandler(req, res) {
|
|||||||
|
|
||||||
switch (controllerVersionMajor) {
|
switch (controllerVersionMajor) {
|
||||||
case 3:
|
case 3:
|
||||||
loginUrl = `${widget.url}/api/user/login?ajax`;
|
loginUrl = `${url}/api/user/login?ajax`;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
loginUrl = `${widget.url}/api/v2/login`;
|
loginUrl = `${url}/api/v2/login`;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
loginUrl = `${widget.url}/${cId}/api/v2/login`;
|
loginUrl = `${url}/${cId}/api/v2/login`;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -105,7 +105,7 @@ export default async function omadaProxyHandler(req, res) {
|
|||||||
|
|
||||||
switch (controllerVersionMajor) {
|
switch (controllerVersionMajor) {
|
||||||
case 3:
|
case 3:
|
||||||
sitesUrl = `${widget.url}/web/v1/controller?ajax=&token=${token}`;
|
sitesUrl = `${url}/web/v1/controller?ajax=&token=${token}`;
|
||||||
body = {
|
body = {
|
||||||
"method": "getUserSites",
|
"method": "getUserSites",
|
||||||
"params": {
|
"params": {
|
||||||
@@ -115,10 +115,10 @@ export default async function omadaProxyHandler(req, res) {
|
|||||||
method = "POST";
|
method = "POST";
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
sitesUrl = `${widget.url}/api/v2/sites?token=${token}¤tPage=1¤tPageSize=1000`;
|
sitesUrl = `${url}/api/v2/sites?token=${token}¤tPage=1¤tPageSize=1000`;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
sitesUrl = `${widget.url}/${cId}/api/v2/sites?token=${token}¤tPage=1¤tPageSize=1000`;
|
sitesUrl = `${url}/${cId}/api/v2/sites?token=${token}¤tPage=1¤tPageSize=1000`;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -133,7 +133,7 @@ export default async function omadaProxyHandler(req, res) {
|
|||||||
|
|
||||||
const sitesResponseData = JSON.parse(data);
|
const sitesResponseData = JSON.parse(data);
|
||||||
|
|
||||||
if (sitesResponseData.errorCode > 0) {
|
if (status !== 200 || sitesResponseData.errorCode > 0) {
|
||||||
logger.debug(`HTTTP ${status} getting sites list: ${sitesResponseData.msg}`);
|
logger.debug(`HTTTP ${status} getting sites list: ${sitesResponseData.msg}`);
|
||||||
return res.status(status).json({error: {message: "Error getting sites list", url, data: sitesResponseData}});
|
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);
|
sitesResponseData.result.data.find(s => s.name === widget.site);
|
||||||
|
|
||||||
if (!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;
|
let siteResponseData;
|
||||||
@@ -156,7 +156,7 @@ export default async function omadaProxyHandler(req, res) {
|
|||||||
|
|
||||||
if (controllerVersionMajor === 3) {
|
if (controllerVersionMajor === 3) {
|
||||||
// Omada v3 controller requires switching site
|
// 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";
|
method = "POST";
|
||||||
body = {
|
body = {
|
||||||
method: "switchSite",
|
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}});
|
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, {
|
[status, contentType, data] = await httpProxy(statsUrl, {
|
||||||
method,
|
method,
|
||||||
params,
|
params,
|
||||||
|
|||||||
@@ -84,9 +84,9 @@ export default async function pyloadProxyHandler(req, res) {
|
|||||||
|
|
||||||
if (data?.error || status !== 200) {
|
if (data?.error || status !== 200) {
|
||||||
try {
|
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) {
|
} 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) {
|
} catch (e) {
|
||||||
logger.error(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' });
|
return res.status(400).json({ error: 'Invalid proxy service type' });
|
||||||
|
|||||||
@@ -1,9 +1,31 @@
|
|||||||
import { jsonArrayFilter } from "utils/proxy/api-helpers";
|
import { jsonArrayFilter } from "utils/proxy/api-helpers";
|
||||||
|
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
|
||||||
import genericProxyHandler from "utils/proxy/handlers/generic";
|
import genericProxyHandler from "utils/proxy/handlers/generic";
|
||||||
|
import getServiceWidget from "utils/config/service-helpers";
|
||||||
|
|
||||||
const widget = {
|
const widget = {
|
||||||
api: "{url}/api/v2.0/{endpoint}",
|
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: {
|
mappings: {
|
||||||
alerts: {
|
alerts: {
|
||||||
|
|||||||
Reference in New Issue
Block a user