Compare commits

...

68 Commits

Author SHA1 Message Date
Ben Phelps
94a518f4a8 add styled headers option 2022-10-08 16:40:36 +03:00
Ben Phelps
e56dccc7f1 new logo and styling tweaks 2022-10-08 16:04:24 +03:00
Anonymous
adf601c572 Translated using Weblate (Finnish)
Currently translated at 96.7% (119 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fi/
2022-10-05 17:56:15 +02:00
Anonymous
00eb768cdf Translated using Weblate (Yue)
Currently translated at 95.9% (118 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/yue/
2022-10-05 17:56:14 +02:00
Anonymous
b8d00f24f7 Translated using Weblate (Portuguese (Brazil))
Currently translated at 93.4% (115 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pt_BR/
2022-10-05 17:56:14 +02:00
Anonymous
031f11c5c1 Translated using Weblate (Romanian)
Currently translated at 95.9% (118 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ro/
2022-10-05 17:56:14 +02:00
Anonymous
1ffe7da9b7 Translated using Weblate (Hebrew)
Currently translated at 82.1% (101 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/he/
2022-10-05 17:56:14 +02:00
Anonymous
292c6dd6b0 Translated using Weblate (Hungarian)
Currently translated at 87.8% (108 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hu/
2022-10-05 17:56:13 +02:00
Anonymous
6f5a0ea6b8 Translated using Weblate (Croatian)
Currently translated at 93.4% (115 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hr/
2022-10-05 17:56:13 +02:00
Anonymous
f44b564bb5 Translated using Weblate (Swedish)
Currently translated at 86.9% (107 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/sv/
2022-10-05 17:56:13 +02:00
Anonymous
b380fd2306 Translated using Weblate (Polish)
Currently translated at 79.6% (98 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pl/
2022-10-05 17:56:13 +02:00
Anonymous
528ce1a5fe Translated using Weblate (Catalan)
Currently translated at 96.7% (119 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ca/
2022-10-05 17:56:12 +02:00
Anonymous
593d8a1cbc Translated using Weblate (Chinese (Traditional))
Currently translated at 7.3% (9 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/zh_Hant/
2022-10-05 17:56:12 +02:00
Anonymous
8f6ea7b49f Translated using Weblate (Dutch)
Currently translated at 51.2% (63 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/nl/
2022-10-05 17:56:12 +02:00
Anonymous
01d005dbbe Translated using Weblate (Vietnamese)
Currently translated at 35.7% (44 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/vi/
2022-10-05 17:56:11 +02:00
Anonymous
45fb059e71 Translated using Weblate (Norwegian Bokmål)
Currently translated at 64.2% (79 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/nb_NO/
2022-10-05 17:56:11 +02:00
Anonymous
40a11bd9ce Translated using Weblate (Italian)
Currently translated at 62.6% (77 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/it/
2022-10-05 17:56:11 +02:00
Anonymous
935f97415f Translated using Weblate (Chinese (Simplified))
Currently translated at 88.6% (109 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/zh_Hans/
2022-10-05 17:56:10 +02:00
Anonymous
969dab9971 Translated using Weblate (Russian)
Currently translated at 18.6% (23 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ru/
2022-10-05 17:56:10 +02:00
Anonymous
193b548d0b Translated using Weblate (Portuguese)
Currently translated at 93.4% (115 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pt/
2022-10-05 17:56:10 +02:00
Anonymous
6dacd1bdd6 Translated using Weblate (French)
Currently translated at 96.7% (119 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fr/
2022-10-05 17:56:09 +02:00
Anonymous
6a0a872790 Translated using Weblate (Spanish)
Currently translated at 96.7% (119 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/es/
2022-10-05 17:56:09 +02:00
Anonymous
fa71586a50 Translated using Weblate (German)
Currently translated at 95.9% (118 of 123 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/de/
2022-10-05 17:56:09 +02:00
Jason Fischer
65c5828456 Merge pull request #335 from JazzFisch/add-proxmox-widget
Add Proxmox widget
2022-10-05 08:55:56 -07:00
Kai Huuhko
892c68856f Translated using Weblate (Finnish)
Currently translated at 100.0% (119 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fi/
2022-10-05 15:24:22 +02:00
Ben Phelps
ea96999377 place error boundaries closer to the source 2022-10-05 13:43:22 +03:00
Jason Fischer
a9fb458f19 Add Proxmox widget 2022-10-04 21:46:48 -07:00
Ben Phelps
88c774339d Merge pull request #333 from JazzFisch/add-error-boundary
Add ErrorBoundary component
2022-10-05 04:22:26 +03:00
Jason Fischer
962e6e576c Add ErrorBoundary component
- wrap a myriad of components in ErrorBoundary

resolves #270
2022-10-04 13:15:49 -07:00
Anonymous
07a28c0841 Translated using Weblate (Finnish)
Currently translated at 100.0% (0 of 0 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fi/
2022-10-04 14:32:40 +02:00
Juan Manuel Bennàssar Carretero
1e7ef54c05 Translated using Weblate (Catalan)
Currently translated at 100.0% (119 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ca/
2022-10-04 14:32:39 +02:00
Juan Manuel Bennàssar Carretero
701270b020 Translated using Weblate (Spanish)
Currently translated at 100.0% (119 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/es/
2022-10-04 14:32:38 +02:00
Kai Huuhko
84f142683f Added translation using Weblate (Finnish) 2022-10-04 14:32:32 +02:00
Ben Phelps
7033652508 Merge pull request #328 from JazzFisch/disable-open-proxy
Remove pages/api/proxy.js as it was an open proxy
2022-10-04 09:41:49 +03:00
Anonymous
6c3489aa3d Translated using Weblate (Yue)
Currently translated at 99.1% (118 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/yue/
2022-10-04 08:41:21 +02:00
Anonymous
82f18c7cff Translated using Weblate (Portuguese (Brazil))
Currently translated at 96.6% (115 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pt_BR/
2022-10-04 08:41:20 +02:00
Anonymous
875eefe71f Translated using Weblate (Romanian)
Currently translated at 99.1% (118 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ro/
2022-10-04 08:41:20 +02:00
Anonymous
1972f2b6db Translated using Weblate (Hebrew)
Currently translated at 84.8% (101 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/he/
2022-10-04 08:41:20 +02:00
Anonymous
dd080d9a04 Translated using Weblate (Hungarian)
Currently translated at 90.7% (108 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hu/
2022-10-04 08:41:20 +02:00
Anonymous
933414934c Translated using Weblate (Croatian)
Currently translated at 96.6% (115 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/hr/
2022-10-04 08:41:20 +02:00
Anonymous
dec25762f0 Translated using Weblate (Swedish)
Currently translated at 89.9% (107 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/sv/
2022-10-04 08:41:19 +02:00
Anonymous
a2c9754560 Translated using Weblate (Polish)
Currently translated at 82.3% (98 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pl/
2022-10-04 08:41:19 +02:00
Anonymous
0f107d8648 Translated using Weblate (Catalan)
Currently translated at 99.1% (118 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ca/
2022-10-04 08:41:19 +02:00
Anonymous
1b7b6af84d Translated using Weblate (Chinese (Traditional))
Currently translated at 7.5% (9 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/zh_Hant/
2022-10-04 08:41:19 +02:00
Anonymous
5e38e71229 Translated using Weblate (Dutch)
Currently translated at 52.9% (63 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/nl/
2022-10-04 08:41:19 +02:00
Anonymous
83983d772d Translated using Weblate (Vietnamese)
Currently translated at 36.9% (44 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/vi/
2022-10-04 08:41:18 +02:00
Anonymous
3e133c10d2 Translated using Weblate (Norwegian Bokmål)
Currently translated at 66.3% (79 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/nb_NO/
2022-10-04 08:41:18 +02:00
Anonymous
6a67873c10 Translated using Weblate (Italian)
Currently translated at 64.7% (77 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/it/
2022-10-04 08:41:18 +02:00
Anonymous
588ea9b04e Translated using Weblate (Chinese (Simplified))
Currently translated at 91.5% (109 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/zh_Hans/
2022-10-04 08:41:18 +02:00
Anonymous
68b7fe2b35 Translated using Weblate (Russian)
Currently translated at 19.3% (23 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ru/
2022-10-04 08:41:18 +02:00
Anonymous
5643af9845 Translated using Weblate (Portuguese)
Currently translated at 96.6% (115 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/pt/
2022-10-04 08:41:17 +02:00
Anonymous
ad3752650b Translated using Weblate (French)
Currently translated at 99.1% (118 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/fr/
2022-10-04 08:41:17 +02:00
Anonymous
8718b4bcee Translated using Weblate (Spanish)
Currently translated at 99.1% (118 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/es/
2022-10-04 08:41:17 +02:00
Anonymous
f29154cfa4 Translated using Weblate (German)
Currently translated at 99.1% (118 of 119 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/de/
2022-10-04 08:41:17 +02:00
Ben Phelps
da948f83a1 Merge pull request #326 from juanmanuelbc/main
Fix resources CPU label...
2022-10-04 09:41:01 +03:00
Jason Fischer
84bb98b007 Update component.jsx 2022-10-03 15:20:30 -07:00
Jason Fischer
c3a623c329 Remove pages/api/proxy.js as it was an open proxy
- The only location pages/api/proxy was used was for icon loading in item.jsx, simply returning the icon URL instead worked just fine.  There was no need to proxy icon requests.

fixes: #327
2022-10-03 15:17:56 -07:00
Juan Manuel Bennàssar Carretero
1249724f8a Fix resources CPU label... 2022-10-03 15:47:46 +02:00
Ángel Fernández Sánchez
4503612bf0 Translated using Weblate (Spanish)
Currently translated at 100.0% (118 of 118 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/es/
2022-10-03 11:21:53 +02:00
Juan Manuel Bennàssar Carretero
9c7a9eb326 Translated using Weblate (Spanish)
Currently translated at 100.0% (118 of 118 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/es/
2022-10-03 11:21:53 +02:00
Ben Phelps
07e99768e6 speedtest widget, catch api errors 2022-10-03 10:45:45 +03:00
Juan Manuel Bennàssar Carretero
a63f71d3ee Translated using Weblate (Catalan)
Currently translated at 100.0% (118 of 118 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/ca/
2022-10-01 16:04:04 +02:00
Juan Manuel Bennàssar Carretero
76f6b3a4a7 Translated using Weblate (Spanish)
Currently translated at 100.0% (118 of 118 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/es/
2022-10-01 16:04:03 +02:00
Ángel Fernández Sánchez
7052951a43 Translated using Weblate (Spanish)
Currently translated at 100.0% (118 of 118 strings)

Translation: Homepage/Homepage
Translate-URL: https://hosted.weblate.org/projects/homepage/homepage/es/
2022-10-01 16:04:03 +02:00
Ben Phelps
09b4de08e3 hopefully improve font rendering 2022-10-01 15:21:13 +03:00
Ben Phelps
82b490c524 fix formatting typos 2022-10-01 10:33:04 +03:00
Ben Phelps
e0bc45f37e Update README.md 2022-10-01 10:31:22 +03:00
Ben Phelps
6e2197a254 update readme 2022-10-01 10:30:54 +03:00
69 changed files with 1107 additions and 190 deletions

View File

@@ -1,12 +1,19 @@
<p align="center">
<br>
<picture>
<source media="(prefers-color-scheme: dark)" srcset="images/homepage-dark.png">
<img src="images/homepage-light.png" width="65%">
<source media="(prefers-color-scheme: dark)" srcset="images/banner_light@2x.png">
<img src="images/banner_dark@2x.png" width="65%">
</picture>
<br><br>
</p>
<p align="center">
A modern <em>(fully static, fast)</em>, secure <em>(fully proxied)</em>, highly customizable application dashboard with integrations for more than 25 services and translations for 15 languages. Easily configured via YAML files (or discovery via docker labels).
</p>
<p align="center">
<img src="images/1.png" />
<br>
</p>
<p align="center">
<img src="images/2.png" width="19%" />
<img src="images/3.png" width="19%" />
<img src="images/4.png" width="19%" />
@@ -20,17 +27,18 @@
</p>
<p align="center">
<a href="https://github.com/benphelps/homepage/actions/workflows/docker-publish.yml"><img src="https://github.com/benphelps/homepage/actions/workflows/docker-publish.yml/badge.svg" alt="Docker"></a>
<a href="https://hosted.weblate.org/engage/homepage/"><img src="https://hosted.weblate.org/widgets/homepage/-/homepage/svg-badge.svg" alt="Weblate"></a>
<a href="https://github.com/benphelps/homepage/actions/workflows/docker-publish.yml"><img src="https://github.com/benphelps/homepage/actions/workflows/docker-publish.yml/badge.svg" alt="Docker"></a>
<a href="https://hosted.weblate.org/engage/homepage/"><img src="https://hosted.weblate.org/widgets/homepage/-/homepage/svg-badge.svg" alt="Weblate"></a>
</p>
## Features
- Fast! The entire site is statically generated at build time, so you can expect instant load times
- **Fast!** The entire site is statically generated at build time, so you can expect instant load times
- **Secure!** Every API request to backend services goes through a proxy server, so your API keys are never exposed to the frontend client.
- Images built for AMD64 (x86_64), ARM64, ARMv7 and ARMv6
- Supports all Raspberry Pi's, most SBCs & Apple Silicon
- Full i18n support with automatic language detection
- Translations for Chinese, Dutch, French, German, Hebrew, Hungarian, Norwegian Bokmål, Polish, Portuguese, Russian, Spanish and Swedish
- Translantions for Chinese, Dutch, Finnish, French, German, Hebrew, Hungarian, Norwegian Bokmål, Polish, Portuguese, Portuguese (Brazil), Romainian, Russian, Spanish, Swedish and Yue
- Want to help translate? [Join the Weblate project](https://hosted.weblate.org/engage/homepage/)
- Service & Web Bookmarks
- Docker Integration
@@ -39,17 +47,17 @@
- 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
- Portainer, Traefik, Speedtest Tracker, PiHole, AdGuard Home, Nginx Proxy Manager, Gotify, Syncthing Relay Server, Authentic
- Information Providers
- Coin Market Cap, Mastodon
- Information & Utility Widgets
- System Stats (Disk, CPU, Memory)
- Weather via WeatherAPI.com or OpenWeatherMap
- Automatic location detection (with HTTPS), or manual location selection
- Search Bar
- Customizable
- 21 theme colors with light and dark mode support
- Background image support
- Column and Row layout options
## Support & Suggestions
@@ -140,26 +148,43 @@ Huge thanks to the all the contributors who have helped make this project what i
- [aidenpwnz](https://github.com/benphelps/homepage/commits?author=aidenpwnz) - Nginx Proxy Manager, Search Bar Widget
- [AlexFullmoon](https://github.com/benphelps/homepage/commits?author=AlexFullmoon) - OpenWeatherMap Widget
- [AmadeusGraves](https://github.com/benphelps/homepage/commits?author=AmadeusGraves) - Spanish Translation
- [andrii-kryvoviaz](https://github.com/benphelps/homepage/commits?author=andrii-kryvoviaz) - Background opacity option
- [boerniee](https://github.com/benphelps/homepage/commits?author=boerniee) - German Translation
- [comradekingu](https://github.com/benphelps/homepage/commits?author=comradekingu) - Norwegian Bokmål Translation
- Daniel Varga - German & Hungarian Translation
- [deffcolony](https://github.com/benphelps/homepage/commits?author=deffcolony) - Dutch Translation
- [desolaris](https://github.com/benphelps/homepage/commits?author=desolaris) - Russian Translation
- [DevPGSV](https://github.com/benphelps/homepage/commits?author=DevPGSV) - Syncthing Relay Server & Mastodon widgets
- [ilusi0n](https://github.com/benphelps/homepage/commits?author=ilusi0n) - Jellyseerr Integration
- [ItsJustMeChris](https://github.com/benphelps/homepage/commits?author=ItsJustMeChris) - Coin Market Cap Widget
- [jackblk](https://github.com/benphelps/homepage/commits?author=jackblk) - Vietnamese Translation
- [JazzFisch](https://github.com/benphelps/homepage/commits?author=JazzFisch) - Readarr, Bazarr, Lidarr, SABnzbd, Transmission & qBittorrent Integrations
- [juanmanuelbc](https://github.com/benphelps/homepage/commits?author=juanmanuelbc) - Spanish and Catalan Translations
- [JazzFisch](https://github.com/benphelps/homepage/commits?author=JazzFisch) - Readarr, Bazarr, Lidarr, SABnzbd, Transmission, qBittorrent Integrations & countless more improvements
- [josways](https://github.com/benphelps/homepage/commits?author=josways) - Baidu search provider
- [mauricio-kalil](https://github.com/benphelps/homepage/commits?author=mauricio-kalil) - Portuguese (Brazil)
- [modem7](https://github.com/benphelps/homepage/commits?author=modem7) - Impvoed Docker Image
- [nicedc](https://github.com/benphelps/homepage/commits?author=nicedc) - Chinese Translation
- [Nonoss117](https://github.com/benphelps/homepage/commits?author=Nonoss117) - French Translation
- [pacoculebras](https://github.com/benphelps/homepage/commits?author=pacoculebras) - Catalan Translation
- [psychodracon](https://github.com/benphelps/homepage/commits?author=psychodracon) - Polish Translation
- [MountainGod2](https://github.com/benphelps/homepage/discussions/243) - Homepage Logo
- [quod](https://github.com/benphelps/homepage/commits?author=quod) - Fixed Typos
- [schklom](https://github.com/benphelps/homepage/commits?author=schklom) - ARM64, ARMv7 and ARMv6
- [ShlomiPorush](https://github.com/benphelps/homepage/commits?author=ShlomiPorush) - Hebrew Translation
- [SuperDOS](https://github.com/benphelps/homepage/commits?author=SuperDOS) - Swedish Translation
- [xicopitz](https://github.com/benphelps/homepage/commits?author=xicopitz) - Gotify & Prowlarr Integration
### Translators
- [3vilson](https://github.com/benphelps/homepage/commits?author=3vilson) - German
- [4lenz1](https://github.com/benphelps/homepage/commits?author=4lenz1) - Chinese
- [AmadeusGraves](https://github.com/benphelps/homepage/commits?author=AmadeusGraves) - Spanish
- [boerniee](https://github.com/benphelps/homepage/commits?author=boerniee) - German
- [brunoccr](https://github.com/benphelps/homepage/commits?author=brunoccr) - Portuguese (Brazil)
- [C8opmBM](https://github.com/benphelps/homepage/commits?author=C8opmBM) - Romainian
- [comradekingu](https://github.com/benphelps/homepage/commits?author=comradekingu) - Norwegian Bokmål
- Daniel Varga - German & Hungarian
- [deffcolony](https://github.com/benphelps/homepage/commits?author=deffcolony) - Dutch
- [desolaris](https://github.com/benphelps/homepage/commits?author=desolaris) - Russian
- [ericlokz](https://github.com/benphelps/homepage/commits?author=ericlokz) - Yue
- [FunsKiTo](https://github.com/benphelps/homepage/commits?author=FunsKiTo) - Spanish
- [jackblk](https://github.com/benphelps/homepage/commits?author=jackblk) - Vietnamese
- [juanmanuelbc](https://github.com/benphelps/homepage/commits?author=juanmanuelbc) - Spanish and Catalan
- [ling0412](https://github.com/benphelps/homepage/commits?author=ling0412) - Chinese
- [milotype](https://github.com/benphelps/homepage/commits?author=milotype) - Croatian
- [nicedc](https://github.com/benphelps/homepage/commits?author=nicedc) - Chinese
- [Nonoss117](https://github.com/benphelps/homepage/commits?author=Nonoss117) - French
- [pacoculebras](https://github.com/benphelps/homepage/commits?author=pacoculebras) - Catalan
- [Prilann](https://github.com/benphelps/homepage/commits?author=Prilann) - German
- [psychodracon](https://github.com/benphelps/homepage/commits?author=psychodracon) - Polish
- Sascha Jelinek - German
- [ShlomiPorush](https://github.com/benphelps/homepage/commits?author=ShlomiPorush) - Hebrew
- [SuperDOS](https://github.com/benphelps/homepage/commits?author=SuperDOS) - Swedish
- [kaihu](https://github.com/benphelps/homepage/commits?author=kaihu) - Finnish

BIN
images/banner_dark@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
images/banner_light@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

BIN
public/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
public/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 B

BIN
public/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

BIN
public/homepage.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -33,13 +33,14 @@
"total": "Total",
"free": "Lliure",
"used": "Usat",
"load": "Càrrega"
"load": "Càrrega",
"cpu": "CPU"
},
"docker": {
"rx": "RX",
"tx": "TX",
"mem": "MEM",
"cpu": "CPU",
"rx": "Rebut",
"tx": "Transmès",
"mem": "Memòria",
"cpu": "Processador",
"offline": "Fora de línia"
},
"emby": {
@@ -170,8 +171,14 @@
"transferRate": "Velocitat"
},
"authentik": {
"users": "Users",
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)"
"users": "Usuaris",
"loginsLast24H": "Inicis de sessió (24h)",
"failedLoginsLast24H": "Errors d'inici de sessió (24h)"
},
"proxmox": {
"vms": "VMs",
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC"
}
}

View File

@@ -11,7 +11,8 @@
"total": "Gesamt",
"free": "Frei",
"used": "Gebraucht",
"load": "Belastung"
"load": "Belastung",
"cpu": "CPU"
},
"docker": {
"rx": "Rx",
@@ -173,5 +174,11 @@
"users": "Benutzer",
"loginsLast24H": "Anmeldungen (24h)",
"failedLoginsLast24H": "fehlerhafte Anmeldungen (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -25,6 +25,7 @@
"placeholder": "Search…"
},
"resources": {
"cpu": "CPU",
"total": "Total",
"free": "Free",
"used": "Used",
@@ -184,5 +185,11 @@
"users": "Users",
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -11,13 +11,14 @@
"total": "Total",
"free": "Libre",
"used": "Usado",
"load": "Carga"
"load": "Carga",
"cpu": "CPU"
},
"docker": {
"rx": "RX",
"tx": "TX",
"mem": "MEM",
"cpu": "CPU",
"rx": "Recibido",
"tx": "Transmitido",
"mem": "Memoria",
"cpu": "Procesador",
"offline": "Desconectado"
},
"emby": {
@@ -171,7 +172,13 @@
},
"authentik": {
"users": "Usuarios",
"loginsLast24H": "Accesos (24h)",
"failedLoginsLast24H": "Accesos Fallidos (24h)"
"loginsLast24H": "Inicios de sesión (24h)",
"failedLoginsLast24H": "Inicios de sesión fallidos (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -0,0 +1,184 @@
{
"widget": {
"missing_type": "Puuttuva härpäkkeen tyyppi: {{type}}",
"api_error": "API-virhe",
"status": "Tila"
},
"weather": {
"current": "Nykyinen sijainti",
"allow": "Klikkaa salliaksesi",
"updating": "Päivitetään",
"wait": "Odota, ole hyvä"
},
"search": {
"placeholder": "Hae…"
},
"resources": {
"cpu": "CPU",
"total": "Yhteensä",
"free": "Vapaana",
"used": "Käytetty",
"load": "Kuorma"
},
"docker": {
"rx": "RX",
"tx": "TX",
"mem": "RAM",
"cpu": "CPU",
"offline": "Offline"
},
"emby": {
"playing": "Toistaa",
"transcoding": "Transkoodaa",
"bitrate": "Bittinopeus",
"no_active": "Ei aktiivisia striimejä"
},
"tautulli": {
"playing": "Toistaa",
"transcoding": "Transkoodaa",
"bitrate": "Bittinopeus",
"no_active": "Ei aktiivisia striimejä"
},
"nzbget": {
"rate": "Nopeus",
"remaining": "Jäljellä",
"downloaded": "Ladattu"
},
"sabnzbd": {
"rate": "Nopeus",
"queue": "Jono",
"timeleft": "Aikaa jäljellä"
},
"rutorrent": {
"active": "Aktiivinen",
"upload": "Lähetys",
"download": "Lataus"
},
"transmission": {
"download": "Lataus",
"upload": "Lähetys",
"leech": "Lataajia",
"seed": "Lähettäjiä"
},
"qbittorrent": {
"download": "Lataus",
"upload": "Lähetys",
"leech": "Lataajia",
"seed": "Lähettäjiä"
},
"sonarr": {
"wanted": "Haluttu",
"queued": "Jonossa",
"series": "Sarja"
},
"radarr": {
"wanted": "Haluttu",
"queued": "Jonossa",
"movies": "Elokuvia"
},
"lidarr": {
"wanted": "Haluttu",
"queued": "Jonossa",
"albums": "Albumeja"
},
"readarr": {
"wanted": "Haluttu",
"queued": "Jonossa",
"books": "Kirjoja"
},
"bazarr": {
"missingEpisodes": "Puuttuvia jaksoja",
"missingMovies": "Puuttuvia elokuvia"
},
"ombi": {
"pending": "Vireillä",
"approved": "Hyväksytty",
"available": "Saatavilla"
},
"jellyseerr": {
"pending": "Vireillä",
"approved": "Hyväksytty",
"available": "Saatavilla"
},
"overseerr": {
"pending": "Vireillä",
"approved": "Hyväksytty",
"available": "Saatavilla"
},
"pihole": {
"queries": "Kyselyjä",
"blocked": "Estetty",
"gravity": "Vakavuus"
},
"adguard": {
"queries": "Kyselyjä",
"blocked": "Estetty",
"filtered": "Suodatettu",
"latency": "Viive"
},
"speedtest": {
"upload": "Lähetys",
"download": "Lataus",
"ping": "Viive"
},
"portainer": {
"running": "Käynnissä",
"stopped": "Pysäytetty",
"total": "Yhteensä"
},
"traefik": {
"routers": "Reitittimiä",
"services": "Palveluja",
"middleware": "Middlewareja"
},
"npm": {
"enabled": "Käytössä",
"disabled": "Poissa käytöstä",
"total": "Yhteensä"
},
"coinmarketcap": {
"configure": "Määritä yksi tai useampi kryptovaluutta seurattavaksi",
"1hour": "1 tunti",
"1day": "1 päivä",
"7days": "7 päivää",
"30days": "30 päivää"
},
"gotify": {
"apps": "Sovelluksia",
"clients": "Asiakasohjelmia",
"messages": "Viestejä"
},
"prowlarr": {
"enableIndexers": "Indeksoijia",
"numberOfGrabs": "Nappauksia",
"numberOfQueries": "Hakuja",
"numberOfFailGrabs": "Epäonnistuneita nappauksia",
"numberOfFailQueries": "Epäonnistuneita kyselyjä"
},
"jackett": {
"configured": "Määritettyjä",
"errored": "Virheellisiä"
},
"strelaysrv": {
"numActiveSessions": "Istuntoja",
"numConnections": "Yhteyksiä",
"dataRelayed": "Välitetty",
"transferRate": "Nopeus"
},
"mastodon": {
"user_count": "Käyttäjiä",
"status_count": "Kirjoituksia",
"domain_count": "Verkkotunnuksia"
},
"authentik": {
"users": "Käyttäjiä",
"loginsLast24H": "Kirjautumisia (24h)",
"failedLoginsLast24H": "Epäonnistuneita kirjautumisia (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -11,7 +11,8 @@
"total": "Total",
"free": "Libre",
"used": "Utilisé",
"load": "Charge"
"load": "Charge",
"cpu": "CPU"
},
"docker": {
"rx": "Rx",
@@ -173,5 +174,11 @@
"users": "Utilisateurs",
"loginsLast24H": "Cnx. (24h)",
"failedLoginsLast24H": "Cnx. échouées (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -17,7 +17,8 @@
"total": "סה\"כ",
"free": "פנוי",
"used": "בשימוש",
"load": "עומס"
"load": "עומס",
"cpu": "CPU"
},
"docker": {
"rx": "RX",
@@ -173,5 +174,11 @@
"users": "Users",
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -12,7 +12,8 @@
"total": "Ukupno",
"free": "Slobodno",
"used": "Korišteno",
"load": "Opterećenje"
"load": "Opterećenje",
"cpu": "CPU"
},
"sabnzbd": {
"rate": "Stopa",
@@ -173,5 +174,11 @@
"users": "Users",
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -3,7 +3,8 @@
"total": "Összes",
"free": "Szabad",
"used": "Használt",
"load": "Terhelés"
"load": "Terhelés",
"cpu": "CPU"
},
"docker": {
"rx": "RX",
@@ -173,5 +174,11 @@
"users": "Users",
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -45,7 +45,8 @@
"total": "Totale",
"free": "Libero",
"used": "In utilizzo",
"load": "Load"
"load": "Load",
"cpu": "CPU"
},
"rutorrent": {
"active": "Attivo",
@@ -173,5 +174,11 @@
"users": "Users",
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -11,7 +11,8 @@
"total": "Totalt",
"free": "Ledig",
"used": "Brukt",
"load": "Last inn"
"load": "Last inn",
"cpu": "CPU"
},
"docker": {
"rx": "Mottatt",
@@ -173,5 +174,11 @@
"users": "Users",
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -8,7 +8,8 @@
"total": "Totaal",
"free": "Vrij",
"used": "Gebruikt",
"load": "Load"
"load": "Load",
"cpu": "CPU"
},
"docker": {
"rx": "RX",
@@ -173,5 +174,11 @@
"users": "Users",
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -12,7 +12,8 @@
"used": "Użyte",
"load": "Obciążenie",
"total": "Całkowite",
"free": "Wolne"
"free": "Wolne",
"cpu": "CPU"
},
"emby": {
"no_active": "Brak aktywnych strumieni",
@@ -173,5 +174,11 @@
"users": "Users",
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -45,7 +45,8 @@
"total": "Total",
"free": "Livre",
"used": "Usado",
"load": "Carregamento"
"load": "Carregamento",
"cpu": "CPU"
},
"docker": {
"rx": "Rx",
@@ -173,5 +174,11 @@
"users": "Users",
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -11,7 +11,8 @@
"total": "Total",
"free": "Livre",
"used": "Usado",
"load": "Carregar"
"load": "Carregar",
"cpu": "CPU"
},
"docker": {
"rx": "Rx",
@@ -184,5 +185,11 @@
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)",
"users": "Users"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -3,7 +3,8 @@
"used": "Utilizați",
"load": "Sarcină",
"total": "Total",
"free": "Disponibili"
"free": "Disponibili",
"cpu": "CPU"
},
"docker": {
"rx": "RX",
@@ -173,5 +174,11 @@
"users": "Utilizatori",
"loginsLast24H": "Autentificări (24h)",
"failedLoginsLast24H": "Conectări eșuate (24h)"
},
"proxmox": {
"vms": "VMs",
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC"
}
}

View File

@@ -11,7 +11,8 @@
"total": "Всего",
"free": "Свободно",
"used": "Использовано",
"load": "Load"
"load": "Load",
"cpu": "CPU"
},
"docker": {
"rx": "Rx",
@@ -173,5 +174,11 @@
"users": "Users",
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -14,7 +14,8 @@
"load": "Laddar",
"total": "Total",
"free": "Ledigt",
"used": "Använt"
"used": "Använt",
"cpu": "CPU"
},
"docker": {
"rx": "RX",
@@ -173,5 +174,11 @@
"users": "Users",
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -11,7 +11,8 @@
"total": "Tổng",
"free": "Dư",
"used": "Đã dùng",
"load": "Load"
"load": "Load",
"cpu": "CPU"
},
"docker": {
"rx": "RX",
@@ -173,5 +174,11 @@
"users": "Users",
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -35,7 +35,8 @@
"total": "全部",
"free": "剩餘",
"used": "用咗",
"load": "負荷"
"load": "負荷",
"cpu": "CPU"
},
"docker": {
"rx": "接收",
@@ -173,5 +174,11 @@
"users": "用戶",
"loginsLast24H": "登錄( 24小时",
"failedLoginsLast24H": "登錄失敗( 24鐘頭"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -11,7 +11,8 @@
"total": "共",
"free": "空闲",
"used": "已用",
"load": "负载"
"load": "负载",
"cpu": "CPU"
},
"docker": {
"rx": "接收",
@@ -173,5 +174,11 @@
"users": "Users",
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

View File

@@ -41,7 +41,8 @@
"total": "Total",
"free": "Free",
"used": "Used",
"load": "Load"
"load": "Load",
"cpu": "CPU"
},
"nzbget": {
"rate": "Rate",
@@ -173,5 +174,11 @@
"users": "Users",
"loginsLast24H": "Logins (24h)",
"failedLoginsLast24H": "Failed Logins (24h)"
},
"proxmox": {
"mem": "MEM",
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
}
}

BIN
public/mstile-144x144.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
public/mstile-150x150.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
public/mstile-310x150.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
public/mstile-310x310.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
public/mstile-70x70.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 958 B

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="700.000000pt" height="700.000000pt" viewBox="0 0 700.000000 700.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,700.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1682 5688 c-141 -24 -253 -147 -287 -316 -5 -24 -10 -3658 -5 -4029
0 -33 0 -33 168 108 92 77 178 151 192 162 14 12 75 64 136 114 62 51 114 99
116 105 2 7 4 209 3 449 l-1 436 276 0 c151 0 277 -4 280 -8 3 -4 5 -98 4
-208 0 -111 2 -201 4 -201 3 0 117 95 132 110 3 3 73 61 155 130 168 140 167
139 250 209 51 43 62 48 75 37 13 -12 524 -441 654 -550 28 -22 68 -57 91 -76
23 -19 66 -55 95 -80 30 -25 86 -72 125 -105 38 -33 140 -118 225 -190 85 -71
166 -139 180 -151 14 -13 70 -60 124 -106 55 -46 133 -111 174 -145 41 -35 77
-61 80 -59 4 2 7 607 7 1344 -1 1197 1 1341 15 1344 8 2 163 3 343 3 l328 0
-4 685 c-1 377 -6 695 -9 706 -26 90 -93 186 -161 233 -86 58 30 55 -1924 55
-991 1 -1819 -2 -1841 -6z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,10 +1,13 @@
import ErrorBoundary from "components/errorboundry";
import List from "components/bookmarks/list";
export default function BookmarksGroup({ group }) {
return (
<div key={group.name} className="basis-full md:basis-1/2 lg:basis-1/3 xl:basis-1/4 flex-1 p-1">
<div key={group.name} className="basis-full md:basis-1/2 lg:basis-1/3 xl:basis-1/4 flex-1">
<h2 className="text-theme-800 dark:text-theme-300 text-xl font-medium">{group.name}</h2>
<List bookmarks={group.bookmarks} />
<ErrorBoundary>
<List bookmarks={group.bookmarks} />
</ErrorBoundary>
</div>
);
}

View File

@@ -0,0 +1,40 @@
import React from "react";
export default class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { error: null, errorInfo: null };
}
componentDidCatch(error, errorInfo) {
// Catch errors in any components below and re-render with error message
this.setState({
error,
errorInfo,
});
// You can also log error messages to an error reporting service here
// eslint-disable-next-line no-console
console.error(error, errorInfo);
}
render() {
const { error, errorInfo } = this.state;
if (errorInfo) {
// Error path
return (
<div className="inline-block text-sm bg-rose-100 text-rose-900 dark:bg-rose-900 dark:text-rose-100 rounded-md p-2 m-1">
<div className="font-medium mb-1">Something went wrong.</div>
<details className="text-xs font-mono whitespace-pre">
<summary>{error && error.toString()}</summary>
{errorInfo.componentStack}
</details>
</div>
);
}
// Normally, just render children
const { children } = this.props;
return children;
}
}

114
src/components/favicon.jsx Normal file
View File

@@ -0,0 +1,114 @@
/* eslint-disable @next/next/no-img-element */
/* eslint-disable jsx-a11y/alt-text */
import { useRef, useEffect, useContext } from "react";
import themes from "utils/styles/themes";
import { ColorContext } from "utils/contexts/color";
export function Svg({ svgRef = null }) {
const { color } = useContext(ColorContext);
const { iconStart, iconEnd } = themes[color];
return (
<svg
ref={svgRef}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1024 1024"
style={{
enableBackground: "new 0 0 1024 1024",
}}
xmlSpace="preserve"
className="w-full h-full"
>
<style>
{
".st0{display:none}.st3{stroke-linecap:square}.st3,.st4{fill:none;stroke:#fff;stroke-miterlimit:10}.st6{display:inline;fill:#333}.st7{fill:#fff}"
}
</style>
<g id="Icon">
<path
d="M771.9 191c27.7 0 50.1 26.5 50.1 59.3v186.4l-100.2.3V250.3c0-32.8 22.4-59.3 50.1-59.3z"
style={{
fill: iconStart,
}}
/>
<linearGradient
id="homepage_favicon_gradient"
gradientUnits="userSpaceOnUse"
x1={200.746}
y1={225.015}
x2={764.986}
y2={789.255}
>
<stop
offset={0}
style={{
stopColor: iconStart,
}}
/>
<stop
offset={1}
style={{
stopColor: iconEnd,
}}
/>
</linearGradient>
<path
d="M721.8 250.3c0-32.7 22.4-59.3 50.1-59.3H253.1c-27.7 0-50.1 26.5-50.1 59.3v582.2l90.2-75.7-.1-130.3H375v61.8l88-73.8 258.8 217.9V250.6"
style={{
fill: "url(#homepage_favicon_gradient})",
}}
/>
</g>
</svg>
);
}
export default function Favicon() {
const svgRef = useRef();
const imgRef = useRef();
const canvasRef = useRef();
useEffect(() => {
const svg = svgRef.current;
const img = imgRef.current;
const canvas = canvasRef.current;
if (!svg || !img || !canvas) {
return;
}
const xml = new XMLSerializer().serializeToString(svg);
const svg64 = Buffer.from(xml).toString("base64");
const b64Start = "data:image/svg+xml;base64,";
// prepend a "header"
const image64 = b64Start + svg64;
// set it as the source of the img element
img.onload = () => {
// draw the image onto the canvas
canvas.getContext("2d").drawImage(img, 0, 0);
// canvas.width = 256;
// canvas.height = 256;
const link = window.document.createElement("link");
link.type = "image/x-icon";
link.rel = "shortcut icon";
link.href = canvas.toDataURL("image/x-icon");
document.getElementsByTagName("head")[0].appendChild(link);
};
img.src = image64;
}, []);
return (
<div className="hidden">
<Svg svgRef={svgRef} />
<img width={64} height={64} ref={imgRef} />
<canvas width={64} height={64} ref={canvasRef} />
</div>
);
}

View File

@@ -10,7 +10,7 @@ import { SettingsContext } from "utils/contexts/settings";
function resolveIcon(icon) {
if (icon.startsWith("http")) {
return `/api/proxy?url=${encodeURIComponent(icon)}`;
return icon;
}
if (icon.startsWith("/")) {

View File

@@ -1,5 +1,6 @@
import { useTranslation } from "next-i18next";
import ErrorBoundary from "components/errorboundry";
import components from "widgets/components";
export default function Widget({ service }) {
@@ -8,7 +9,11 @@ export default function Widget({ service }) {
const ServiceWidget = components[service.widget.type];
if (ServiceWidget) {
return <ServiceWidget service={service} />;
return (
<ErrorBoundary>
<ServiceWidget service={service} />
</ErrorBoundary>
);
}
return (

View File

@@ -0,0 +1,56 @@
export default function Logo() {
return (
<div className="w-12 h-12 flex flex-row items-center align-middle mr-3 self-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1024 1024"
style={{
enableBackground: "new 0 0 1024 1024",
}}
xmlSpace="preserve"
className="w-full h-full"
>
<style>
{
".st0{display:none}.st3{stroke-linecap:square}.st3,.st4{fill:none;stroke:#fff;stroke-miterlimit:10}.st6{display:inline;fill:#333}.st7{fill:#fff}"
}
</style>
<g id="Icon">
<path
d="M771.9 191c27.7 0 50.1 26.5 50.1 59.3v186.4l-100.2.3V250.3c0-32.8 22.4-59.3 50.1-59.3z"
style={{
fill: "rgba(var(--color-logo-start))",
}}
/>
<linearGradient
id="homepage_logo_gradient"
gradientUnits="userSpaceOnUse"
x1={200.746}
y1={225.015}
x2={764.986}
y2={789.255}
>
<stop
offset={0}
style={{
stopColor: "rgba(var(--color-logo-start))",
}}
/>
<stop
offset={1}
style={{
stopColor: "rgba(var(--color-logo-stop))",
}}
/>
</linearGradient>
<path
d="M721.8 250.3c0-32.7 22.4-59.3 50.1-59.3H253.1c-27.7 0-50.1 26.5-50.1 59.3v582.2l90.2-75.7-.1-130.3H375v61.8l88-73.8 258.8 217.9V250.6"
style={{
fill: "url(#homepage_logo_gradient)",
}}
/>
</g>
</svg>
</div>
);
}

View File

@@ -16,9 +16,9 @@ function Widget({ options }) {
if (error || data?.cod === 401 || data?.error) {
return (
<div className="flex flex-col justify-center first:ml-0 ml-4">
<div className="flex flex-col justify-center first:ml-auto ml-4 mr-2">
<div className="flex flex-row items-center justify-end">
<div className="flex flex-col items-center">
<div className="hidden sm:flex flex-col items-center">
<BiError className="w-8 h-8 text-theme-800 dark:text-theme-200" />
<div className="flex flex-col ml-3 text-left">
<span className="text-theme-800 dark:text-theme-200 text-sm">{t("widget.api_error")}</span>
@@ -32,9 +32,9 @@ function Widget({ options }) {
if (!data) {
return (
<div className="flex flex-col justify-center first:ml-0 ml-4">
<div className="flex flex-col justify-center first:ml-auto ml-4 mr-2">
<div className="flex flex-row items-center justify-end">
<div className="flex flex-col items-center">
<div className="hidden sm:flex flex-col items-center">
<WiCloudDown className="w-8 h-8 text-theme-800 dark:text-theme-200" />
</div>
<div className="flex flex-col ml-3 text-left">
@@ -49,9 +49,9 @@ function Widget({ options }) {
const unit = options.units === "metric" ? "celsius" : "fahrenheit";
return (
<div className="flex flex-col justify-center first:ml-0 ml-4">
<div className="flex flex-col justify-center first:ml-auto ml-2 mr-2">
<div className="flex flex-row items-center justify-end">
<div className="flex flex-col items-center">
<div className="hidden sm:flex flex-col items-center">
<Icon
condition={data.weather[0].id}
timeOfDay={data.dt > data.sys.sunrise && data.dt < data.sys.sundown ? "day" : "night"}
@@ -102,9 +102,13 @@ export default function OpenWeatherMap({ options }) {
if (!location) {
return (
<button type="button" onClick={() => requestLocation()} className="flex flex-col justify-center first:ml-0 ml-4">
<button
type="button"
onClick={() => requestLocation()}
className="flex flex-col justify-center first:ml-auto ml-4 mr-2"
>
<div className="flex flex-row items-center justify-end">
<div className="flex flex-col items-center">
<div className="hidden sm:flex flex-col items-center">
{requesting ? (
<MdLocationSearching className="w-6 h-6 text-theme-800 dark:text-theme-200 animate-pulse" />
) : (

View File

@@ -30,7 +30,7 @@ export default function Cpu({ expanded }) {
<div className="flex flex-col ml-3 text-left min-w-[85px]">
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
<div className="pl-0.5">-</div>
<div className="pr-1">{t("docker.cpu")}</div>
<div className="pr-1">{t("resources.cpu")}</div>
</div>
{expanded && (
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
@@ -59,7 +59,7 @@ export default function Cpu({ expanded }) {
maximumFractionDigits: 0,
})}
</div>
<div className="pr-1">{t("docker.cpu")}</div>
<div className="pr-1">{t("resources.cpu")}</div>
</div>
{expanded && (
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">

View File

@@ -5,7 +5,7 @@ import Memory from "./memory";
export default function Resources({ options }) {
const { expanded } = options;
return (
<div className="flex flex-col max-w:full sm:basis-auto self-center m-auto flex-wrap">
<div className="flex flex-col max-w:full sm:basis-auto self-center grow-0 flex-wrap">
<div className="flex flex-row self-center flex-wrap justify-between">
{options.cpu && <Cpu expanded={expanded} />}
{options.memory && <Memory expanded={expanded} />}

View File

@@ -56,7 +56,7 @@ export default function Search({ options }) {
}
return (
<form className="flex-col relative h-8 my-4 min-w-full md:min-w-fit grow first:ml-0 ml-4" onSubmit={handleSubmit}>
<form className="flex-col relative h-8 my-4 min-w-fit grow first:ml-0 ml-4" onSubmit={handleSubmit}>
<div className="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none w-full text-theme-800 dark:text-white" />
<input
type="text"

View File

@@ -16,7 +16,7 @@ function Widget({ options }) {
if (error || data?.error) {
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 mr-2">
<div className="flex flex-row items-center justify-end">
<div className="flex flex-col items-center">
<BiError className="w-8 h-8 text-theme-800 dark:text-theme-200" />
@@ -32,7 +32,7 @@ function Widget({ options }) {
if (!data) {
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 mr-2">
<div className="flex flex-row items-center justify-end">
<div className="flex flex-col items-center">
<WiCloudDown className="w-8 h-8 text-theme-800 dark:text-theme-200" />
@@ -49,7 +49,7 @@ function Widget({ options }) {
const unit = options.units === "metric" ? "celsius" : "fahrenheit";
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 mr-2">
<div className="flex flex-row items-center justify-end">
<div className="flex flex-col items-center">
<Icon condition={data.current.condition.code} timeOfDay={data.current.is_day ? "day" : "night"} />
@@ -103,7 +103,11 @@ export default function WeatherApi({ options }) {
if (!location) {
return (
<button type="button" onClick={() => requestLocation()} className="flex flex-col justify-center first:ml-0 ml-4">
<button
type="button"
onClick={() => requestLocation()}
className="flex flex-col justify-center first:ml-0 ml-4 mr-2"
>
<div className="flex flex-row items-center justify-end">
<div className="flex flex-col items-center">
{requesting ? (

View File

@@ -1,5 +1,7 @@
import dynamic from "next/dynamic";
import ErrorBoundary from "components/errorboundry";
const widgetMappings = {
weatherapi: dynamic(() => import("components/widgets/weather/weather")),
openweathermap: dynamic(() => import("components/widgets/openweathermap/weather")),
@@ -7,13 +9,18 @@ const widgetMappings = {
search: dynamic(() => import("components/widgets/search/search")),
greeting: dynamic(() => import("components/widgets/greeting/greeting")),
datetime: dynamic(() => import("components/widgets/datetime/datetime")),
logo: dynamic(() => import("components/widgets/logo/logo"), { ssr: false }),
};
export default function Widget({ widget }) {
const InfoWidget = widgetMappings[widget.type];
if (InfoWidget) {
return <InfoWidget options={widget.options} />;
return (
<ErrorBoundary>
<InfoWidget options={widget.options} />
</ErrorBoundary>
);
}
return (

View File

@@ -8,6 +8,11 @@ export default function Document() {
name="description"
content="A highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations."
/>
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png?v=4" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png?v=4" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png?v=4" />
<link rel="manifest" href="/site.webmanifest?v=4" />
<link rel="mask-icon" href="/safari-pinned-tab.svg?v=4" color="#1e9cd7" />
</Head>
<body>
<Main />

View File

@@ -1,59 +0,0 @@
import https from "https";
import getRawBody from "raw-body";
import { httpRequest, httpsRequest } from "utils/proxy/http";
export const config = {
api: {
bodyParser: false,
},
};
export default async function handler(req, res) {
const headers = ["X-API-Key", "Authorization"].reduce((obj, key) => {
if (req.headers && Object.prototype.hasOwnProperty.call(req.headers, key.toLowerCase())) {
// eslint-disable-next-line no-param-reassign
obj[key] = req.headers[key.toLowerCase()];
}
return obj;
}, {});
const url = new URL(req.query.url);
if (url.protocol === "https:") {
// this agent allows us to bypass the certificate check
// which is required for most self-signed certificates
const httpsAgent = new https.Agent({
rejectUnauthorized: false,
});
const [status, contentType, data] = await httpsRequest(url, {
agent: httpsAgent,
method: req.method,
headers,
body:
req.method === "GET" || req.method === "HEAD"
? null
: await getRawBody(req, {
encoding: "utf8",
}),
});
res.setHeader("Content-Type", contentType);
return res.status(status).send(data);
}
const [status, contentType, data] = await httpRequest(url, {
method: req.method,
headers,
body:
req.method === "GET" || req.method === "HEAD"
? null
: await getRawBody(req, {
encoding: "utf8",
}),
});
res.setHeader("Content-Type", contentType);
return res.status(status).send(data);
}

14
src/pages/api/theme.js Normal file
View File

@@ -0,0 +1,14 @@
import checkAndCopyConfig, { getSettings } from "utils/config/config";
export default function handler({ res }) {
checkAndCopyConfig("settings.yaml");
const settings = getSettings();
const color = settings.color || "slate";
const theme = settings.theme || "dark";
return res.status(200).json({
color,
theme,
});
}

View File

@@ -0,0 +1,29 @@
import { getSettings } from "utils/config/config";
import themes from "utils/styles/themes";
export async function getServerSideProps({ res }) {
const settings = getSettings();
const color = settings.color || "slate";
const theme = settings.theme || "dark";
const xml = `<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png?v=2"/>
<TileColor>${themes[color][theme]}</TileColor>
</tile>
</msapplication>
</browserconfig>`;
res.setHeader("Content-Type", "text/xml");
res.write(xml);
res.end();
return {
props: {},
};
}
export default function BrowserConfig() {}

View File

@@ -19,6 +19,8 @@ import { ColorContext } from "utils/contexts/color";
import { ThemeContext } from "utils/contexts/theme";
import { SettingsContext } from "utils/contexts/settings";
import { bookmarksResponse, servicesResponse, widgetsResponse } from "utils/config/api-response";
import ErrorBoundary from "components/errorboundry";
import themes from "utils/styles/themes";
const ThemeToggle = dynamic(() => import("components/toggles/theme"), {
ssr: false,
@@ -144,11 +146,20 @@ function Index({ initialSettings, fallback }) {
return (
<SWRConfig value={{ fallback, fetcher: (resource, init) => fetch(resource, init).then((res) => res.json()) }}>
<Home initialSettings={initialSettings} />
<ErrorBoundary>
<Home initialSettings={initialSettings} />
</ErrorBoundary>
</SWRConfig>
);
}
const headerStyles = {
boxed:
"m-4 mb-0 sm:m-8 sm:mb-0 rounded-md shadow-md shadow-theme-900/10 dark:shadow-theme-900/20 bg-theme-100/20 dark:bg-white/5 p-3",
underlined: "m-4 mb-0 sm:m-8 sm:mb-1 border-b-2 pb-4 border-theme-800 dark:border-theme-200/50",
clean: "m-4 mb-0 sm:m-8 sm:mb-0",
};
function Home({ initialSettings }) {
const { i18n } = useTranslation();
const { theme, setTheme } = useContext(ThemeContext);
@@ -182,10 +193,24 @@ function Home({ initialSettings }) {
<Head>
<title>{initialSettings.title || "Homepage"}</title>
{initialSettings.base && <base href={initialSettings.base} />}
{initialSettings.favicon && <link rel="icon" href={initialSettings.favicon} />}
{initialSettings.favicon ? (
<link rel="icon" href={initialSettings.favicon} />
) : (
<link rel="shortcut icon" href="/homepage.ico" />
)}
<meta
name="msapplication-TileColor"
content={themes[initialSettings.color || "slate"][initialSettings.theme || "dark"]}
/>
<meta name="theme-color" content={themes[initialSettings.color || "slate"][initialSettings.theme || "dark"]} />
</Head>
<div className="relative container m-auto flex flex-col justify-between z-10">
<div className="flex flex-row flex-wrap m-8 pb-4 mt-10 border-b-2 border-theme-800 dark:border-theme-200 justify-between">
<div
className={classNames(
"flex flex-row flex-wrap justify-between",
headerStyles[initialSettings.headerStyle || "underlined"]
)}
>
{widgets && (
<>
{widgets
@@ -194,7 +219,7 @@ function Home({ initialSettings }) {
<Widget key={i} widget={widget} />
))}
<div className="ml-4 flex flex-wrap basis-full grow sm:basis-auto justify-between md:justify-end mt-2 md:mt-0">
<div className="m-auto sm:ml-2 flex flex-wrap grow sm:basis-auto justify-between md:justify-end">
{widgets
.filter((widget) => rightAlignedWidgets.includes(widget.type))
.map((widget, i) => (
@@ -206,7 +231,7 @@ function Home({ initialSettings }) {
</div>
{services && (
<div className="flex flex-wrap p-8 items-start">
<div className="flex flex-wrap p-4 sm:p-8 sm:pt-4 items-start pb-2">
{services.map((group) => (
<ServicesGroup key={group.name} services={group} layout={initialSettings.layout?.[group.name]} />
))}
@@ -214,7 +239,7 @@ function Home({ initialSettings }) {
)}
{bookmarks && (
<div className="grow flex flex-wrap pt-0 p-8">
<div className="grow flex flex-wrap pt-0 p-4 sm:p-8">
{bookmarks.map((group) => (
<BookmarksGroup key={group.name} group={group} />
))}

View File

@@ -0,0 +1,42 @@
import checkAndCopyConfig, { getSettings } from "utils/config/config";
import themes from "utils/styles/themes";
export async function getServerSideProps({ res }) {
checkAndCopyConfig("settings.yaml");
const settings = getSettings();
const color = settings.color || "slate";
const theme = settings.theme || "dark";
const manifest = {
name: "Homepage",
short_name: "Homepage",
icons: [
{
src: "/android-chrome-192x192.png?v=2",
sizes: "192x192",
type: "image/png",
},
{
src: "/android-chrome-512x512.png?v=2",
sizes: "512x512",
type: "image/png",
},
],
theme_color: themes[color][theme],
background_color: themes[color][theme],
display: "standalone",
};
res.setHeader("Content-Type", "application/manifest+json");
res.write(JSON.stringify(manifest));
res.end();
return {
props: {},
};
}
export default function Webmanifest() {
return null;
}

BIN
src/styles/font/Manrope.ttf Normal file

Binary file not shown.

View File

@@ -13,6 +13,9 @@ html,
body {
font-family: Manrope, "Manrope-Fallback", Arial, sans-serif;
overflow: hidden;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
#page_wrapper {
@@ -36,7 +39,6 @@ body {
#page_container {
overflow: auto;
overflow: overlay;
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
}

View File

@@ -13,49 +13,16 @@
@font-face {
font-family: "Manrope";
font-weight: 200;
src: local("Manrope"), url("./font/Manrope.woff2") format("woff2");
font-weight: 200 800;
font-style: normal;
src: local("Manrope"), url("./font/Manrope.woff2") format("woff2"), url("./font/Manrope.ttf") format("ttf");
font-display: swap;
}
@font-face {
font-family: "Manrope";
font-weight: 300;
src: local("Manrope"), url("./font/Manrope.woff2") format("woff2");
font-display: swap;
}
@font-face {
font-family: "Manrope";
font-weight: 400;
src: local("Manrope"), url("./font/Manrope.woff2") format("woff2");
font-display: swap;
}
@font-face {
font-family: "Manrope";
font-weight: 500;
src: local("Manrope"), url("./font/Manrope.woff2") format("woff2");
font-display: swap;
}
@font-face {
font-family: "Manrope";
font-weight: 600;
src: local("Manrope"), url("./font/Manrope.woff2") format("woff2");
font-display: swap;
}
@font-face {
font-family: "Manrope";
font-weight: 700;
src: local("Manrope"), url("./font/Manrope.woff2") format("woff2");
font-display: swap;
}
@font-face {
font-family: "Manrope";
font-weight: 800;
src: local("Manrope"), url("./font/Manrope.woff2") format("woff2");
font-weight: 200 800;
fint-style: italic;
src: local("Manrope"), url("./font/Manrope.woff2") format("woff2"), url("./font/Manrope.ttf") format("ttf");
font-display: swap;
}

View File

@@ -9,6 +9,9 @@
--color-700: 40 40 40;
--color-800: 255 255 255;
--color-900: 255 255 255;
--color-logo-start: 128 128 128 / 20%;
--color-logo-stop: 128 128 128 / 40%;
}
.theme-slate {
@@ -22,6 +25,9 @@
--color-700: 51 65 85;
--color-800: 30 41 59;
--color-900: 15 23 42;
--color-logo-start: 148 163 184;
--color-logo-stop: 51 65 85;
}
.theme-gray {
@@ -35,6 +41,9 @@
--color-700: 55 65 81;
--color-800: 31 41 55;
--color-900: 17 24 39;
--color-logo-start: 156 163 175;
--color-logo-stop: 55 65 81;
}
.theme-zinc {
@@ -48,6 +57,9 @@
--color-700: 63 63 70;
--color-800: 39 39 42;
--color-900: 24 24 27;
--color-logo-start: 161 161 170;
--color-logo-stop: 63 63 70;
}
.theme-neutral {
@@ -61,6 +73,9 @@
--color-700: 64 64 64;
--color-800: 38 38 38;
--color-900: 23 23 23;
--color-logo-start: 163 163 163;
--color-logo-stop: 64 64 64;
}
.theme-stone {
@@ -74,6 +89,9 @@
--color-700: 68 64 60;
--color-800: 41 37 36;
--color-900: 28 25 23;
--color-logo-start: 168 162 158;
--color-logo-stop: 68 64 60;
}
.theme-red {
@@ -87,6 +105,9 @@
--color-700: 185 28 28;
--color-800: 153 27 27;
--color-900: 127 29 29;
--color-logo-start: 248 113 113;
--color-logo-stop: 185 28 28;
}
.theme-orange {
@@ -100,6 +121,9 @@
--color-700: 194 65 12;
--color-800: 154 52 18;
--color-900: 124 45 18;
--color-logo-start: 251 146 60;
--color-logo-stop: 194 65 12;
}
.theme-amber {
@@ -113,6 +137,9 @@
--color-700: 180 83 9;
--color-800: 146 64 14;
--color-900: 120 53 15;
--color-logo-start: 251 191 36;
--color-logo-stop: 180 83 9;
}
.theme-yellow {
@@ -126,6 +153,9 @@
--color-700: 161 98 7;
--color-800: 133 77 14;
--color-900: 113 63 18;
--color-logo-start: 250 204 21;
--color-logo-stop: 161 98 7;
}
.theme-lime {
@@ -139,6 +169,9 @@
--color-700: 77 124 15;
--color-800: 63 98 18;
--color-900: 54 83 20;
--color-logo-start: 163 230 53;
--color-logo-stop: 77 124 15;
}
.theme-green {
@@ -152,6 +185,9 @@
--color-700: 21 128 61;
--color-800: 22 101 52;
--color-900: 20 83 45;
--color-logo-start: 74 222 128;
--color-logo-stop: 21 128 61;
}
.theme-emerald {
@@ -165,6 +201,9 @@
--color-700: 4 120 87;
--color-800: 6 95 70;
--color-900: 6 78 59;
--color-logo-start: 52 211 153;
--color-logo-stop: 4 120 87;
}
.theme-teal {
@@ -178,6 +217,9 @@
--color-700: 15 118 110;
--color-800: 17 94 89;
--color-900: 19 78 74;
--color-logo-start: 45 212 191;
--color-logo-stop: 15 118 110;
}
.theme-cyan {
@@ -191,6 +233,9 @@
--color-700: 14 116 144;
--color-800: 21 94 117;
--color-900: 22 78 99;
--color-logo-start: 34 211 238;
--color-logo-stop: 14 116 144;
}
.theme-sky {
@@ -204,6 +249,9 @@
--color-700: 3 105 161;
--color-800: 7 89 133;
--color-900: 12 74 110;
--color-logo-start: 56 189 248;
--color-logo-stop: 3 105 161;
}
.theme-blue {
@@ -217,6 +265,9 @@
--color-700: 29 78 216;
--color-800: 30 64 175;
--color-900: 30 58 138;
--color-logo-start: 96 165 250;
--color-logo-stop: 29 78 216;
}
.theme-indigo {
@@ -230,6 +281,9 @@
--color-700: 67 56 202;
--color-800: 55 48 163;
--color-900: 49 46 129;
--color-logo-start: 129 140 248;
--color-logo-stop: 67 56 202;
}
.theme-violet {
@@ -243,6 +297,9 @@
--color-700: 109 40 217;
--color-800: 91 33 182;
--color-900: 76 29 149;
--color-logo-start: 167 139 250;
--color-logo-stop: 109 40 217;
}
.theme-purple {
@@ -256,6 +313,9 @@
--color-700: 126 34 206;
--color-800: 107 33 168;
--color-900: 88 28 135;
--color-logo-start: 192 132 252;
--color-logo-stop: 126 34 206;
}
.theme-fuchsia {
@@ -269,6 +329,9 @@
--color-700: 162 28 175;
--color-800: 134 25 143;
--color-900: 112 26 117;
--color-logo-start: 232 121 249;
--color-logo-stop: 162 28 175;
}
.theme-pink {
@@ -282,6 +345,9 @@
--color-700: 190 24 93;
--color-800: 157 23 77;
--color-900: 131 24 67;
--color-logo-start: 244 114 182;
--color-logo-stop: 190 24 93;
}
.theme-rose {
@@ -295,4 +361,7 @@
--color-700: 190 18 60;
--color-800: 159 18 57;
--color-900: 136 19 55;
--color-logo-start: 251 113 133;
--color-logo-stop: 190 18 60;
}

View File

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

142
src/utils/styles/themes.js Normal file
View File

@@ -0,0 +1,142 @@
const themes = {
white: {
light: "#ffffff",
iconStart: "#ffffff",
iconEnd: "#282828",
dark: "#ffffff",
},
slate: {
light: "#f8fafc",
iconStart: "#94a3b8",
iconEnd: "#334155",
dark: "#1e293b",
},
gray: {
light: "#f9fafb",
iconStart: "#9ca3af",
iconEnd: "#374151",
dark: "#1f2937",
},
zinc: {
light: "#fafafa",
iconStart: "#a1a1aa",
iconEnd: "#3f3f46",
dark: "#27272a",
},
neutral: {
light: "#fafafa",
iconStart: "#a3a3a3",
iconEnd: "#404040",
dark: "#262626",
},
stone: {
light: "#fafaf9",
iconStart: "#a8a29e",
iconEnd: "#44403c",
dark: "#292524",
},
red: {
light: "#fef2f2",
iconStart: "#f87171",
iconEnd: "#b91c1c",
dark: "#991b1b",
},
orange: {
light: "#fff7ed",
iconStart: "#fb923c",
iconEnd: "#c2410c",
dark: "#9a3412",
},
amber: {
light: "#fffbeb",
iconStart: "#fbbf24",
iconEnd: "#b45309",
dark: "#92400e",
},
yellow: {
light: "#fefce8",
iconStart: "#facc15",
iconEnd: "#a16207",
dark: "#854d0e",
},
lime: {
light: "#f7fee7",
iconStart: "#a3e635",
iconEnd: "#4d7c0f",
dark: "#3f6212",
},
green: {
light: "#f0fdf4",
iconStart: "#4ade80",
iconEnd: "#15803d",
dark: "#166534",
},
emerald: {
light: "#ecfdf5",
iconStart: "#34d399",
iconEnd: "#047857",
dark: "#065f46",
},
teal: {
light: "#f0fdfa",
iconStart: "#2dd4bf",
iconEnd: "#0f766e",
dark: "#115e59",
},
cyan: {
light: "#ecfeff",
iconStart: "#22d3ee",
iconEnd: "#0e7490",
dark: "#155e75",
},
sky: {
light: "#f0f9ff",
iconStart: "#38bdf8",
iconEnd: "#0369a1",
dark: "#075985",
},
blue: {
light: "#eff6ff",
iconStart: "#60a5fa",
iconEnd: "#1d4ed8",
dark: "#1e40af",
},
indigo: {
light: "#eef2ff",
iconStart: "#818cf8",
iconEnd: "#4338ca",
dark: "#3730a3",
},
violet: {
light: "#f5f3ff",
iconStart: "#a78bfa",
iconEnd: "#6d28d9",
dark: "#5b21b6",
},
purple: {
light: "#faf5ff",
iconStart: "#c084fc",
iconEnd: "#7e22ce",
dark: "#6b21a8",
},
fuchsia: {
light: "#fdf4ff",
iconStart: "#e879f9",
iconEnd: "#a21caf",
dark: "#86198f",
},
pink: {
light: "#fdf2f8",
iconStart: "#f472b6",
iconEnd: "#be185d",
dark: "#9d174d",
},
rose: {
light: "#fff1f2",
iconStart: "#fb7185",
iconEnd: "#be123c",
dark: "#9f1239",
},
};
export default themes;

View File

@@ -20,6 +20,7 @@ const components = {
pihole: dynamic(() => import("./pihole/component")),
portainer: dynamic(() => import("./portainer/component")),
prowlarr: dynamic(() => import("./prowlarr/component")),
proxmox: dynamic(() => import("./proxmox/component")),
qbittorrent: dynamic(() => import("./qbittorrent/component")),
radarr: dynamic(() => import("./radarr/component")),
readarr: dynamic(() => import("./readarr/component")),

View File

@@ -0,0 +1,53 @@
import { useTranslation } from "next-i18next";
import Container from "components/services/widget/container";
import Block from "components/services/widget/block";
import useWidgetAPI from "utils/proxy/use-widget-api";
function calcRunning(total, current) {
return current.status === "running" ? total + 1 : total;
}
export default function Component({ service }) {
const { t } = useTranslation();
const { widget } = service;
const { data: clusterData, error: clusterError } = useWidgetAPI(widget, "cluster/resources");
if (clusterError) {
return <Container error={t("widget.api_error")} />;
}
if (!clusterData || !clusterData.data) {
return (
<Container service={service}>
<Block label="proxmox.vms" />
<Block label="proxmox.lxc" />
<Block label="proxmox.cpu" />
<Block label="proxmox.ram" />
</Container>
);
}
const { data } = clusterData ;
const vms = data.filter(item => item.type === "qemu") || [];
const lxc = data.filter(item => item.type === "lxc") || [];
const nodes = data.filter(item => item.type === "node") || [];
const runningVMs = vms.reduce(calcRunning, 0);
const runningLXC = lxc.reduce(calcRunning, 0);
// TODO: support more than one node
// TODO: better handling of cluster with zero nodes
const node = nodes.length > 0 ? nodes[0] : { cpu: 0.0, mem: 0, maxmem: 0 };
return (
<Container service={service}>
<Block label="proxmox.vms" value={`${runningVMs} / ${vms.length}`} />
<Block label="proxmox.lxc" value={`${runningLXC} / ${lxc.length}`} />
<Block label="proxmox.cpu" value={t("common.percent", { value: (node.cpu * 100) })} />
<Block label="proxmox.mem" value={t("common.percent", { value: ((node.mem / node.maxmem) * 100) })} />
</Container>
);
}

View File

@@ -0,0 +1,14 @@
import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
const widget = {
api: "{url}/api2/json/{endpoint}",
proxyHandler: credentialedProxyHandler,
mappings: {
"cluster/resources": {
endpoint: "cluster/resources",
},
},
};
export default widget;

View File

@@ -11,7 +11,7 @@ export default function Component({ service }) {
const { data: speedtestData, error: speedtestError } = useWidgetAPI(widget, "speedtest/latest");
if (speedtestError) {
if (speedtestError || (speedtestData && !speedtestData.data)) {
return <Container error={t("widget.api_error")} />;
}
@@ -31,10 +31,7 @@ export default function Component({ service }) {
label="speedtest.download"
value={t("common.bitrate", { value: speedtestData.data.download * 1024 * 1024 })}
/>
<Block
label="speedtest.upload"
value={t("common.bitrate", { value: speedtestData.data.upload * 1024 * 1024 })}
/>
<Block label="speedtest.upload" value={t("common.bitrate", { value: speedtestData.data.upload * 1024 * 1024 })} />
<Block
label="speedtest.ping"
value={t("common.ms", {

View File

@@ -15,6 +15,7 @@ import overseerr from "./overseerr/widget";
import pihole from "./pihole/widget";
import portainer from "./portainer/widget";
import prowlarr from "./prowlarr/widget";
import proxmox from "./proxmox/widget";
import qbittorrent from "./qbittorrent/widget";
import radarr from "./radarr/widget";
import readarr from "./readarr/widget";
@@ -46,6 +47,7 @@ const widgets = {
pihole,
portainer,
prowlarr,
proxmox,
qbittorrent,
radarr,
readarr,