From 7d8c36a7715d243570cd37c4fe8129b40c48a2bf Mon Sep 17 00:00:00 2001 From: SDGDen Date: Thu, 5 Mar 2026 06:34:27 +0100 Subject: [PATCH] including some more dashboard components --- stacks/dashboard/config/www/.env | 69 ++++ .../www/SupportedApps/Bookstack/Bookstack.php | 83 +++++ .../www/SupportedApps/Bookstack/app.json | 10 + .../SupportedApps/Bookstack/config.blade.php | 22 ++ .../Bookstack/livestats.blade.php | 8 + .../www/SupportedApps/Dockge/Dockge.php | 7 + .../config/www/SupportedApps/Dockge/app.json | 10 + .../www/SupportedApps/Dozzle/Dozzle.php | 7 + .../config/www/SupportedApps/Dozzle/app.json | 10 + .../SupportedApps/FileBrowser/FileBrowser.php | 101 ++++++ .../www/SupportedApps/FileBrowser/app.json | 10 + .../FileBrowser/config.blade.php | 29 ++ .../FileBrowser/livestats.blade.php | 10 + .../www/SupportedApps/ITTools/ITTools.php | 7 + .../config/www/SupportedApps/ITTools/app.json | 10 + .../SupportedApps/JDownloader/JDownloader.php | 7 + .../www/SupportedApps/JDownloader/app.json | 10 + .../www/SupportedApps/Jellyfin/Jellyfin.php | 71 ++++ .../www/SupportedApps/Jellyfin/app.json | 10 + .../SupportedApps/Jellyfin/config.blade.php | 18 + .../Jellyfin/livestats.blade.php | 8 + .../NginxProxyManager/NginxProxyManager.php | 61 ++++ .../SupportedApps/NginxProxyManager/app.json | 10 + .../NginxProxyManager/config.blade.php | 18 + .../NginxProxyManager/livestats.blade.php | 10 + .../SupportedApps/OnlyOffice/OnlyOffice.php | 7 + .../www/SupportedApps/OnlyOffice/app.json | 10 + .../www/SupportedApps/Pihole/Pihole.php | 184 +++++++++++ .../config/www/SupportedApps/Pihole/app.json | 10 + .../www/SupportedApps/Pihole/config.blade.php | 43 +++ .../SupportedApps/Pihole/livestats.blade.php | 10 + .../SupportedApps/UptimeKuma/UptimeKuma.php | 95 ++++++ .../www/SupportedApps/UptimeKuma/app.json | 10 + .../SupportedApps/UptimeKuma/config.blade.php | 32 ++ .../UptimeKuma/livestats.blade.php | 10 + .../SupportedApps/Vaultwarden/Vaultwarden.php | 7 + .../www/SupportedApps/Vaultwarden/app.json | 10 + .../www/SupportedApps/WireGuard/WireGuard.php | 7 + .../www/SupportedApps/WireGuard/app.json | 10 + .../www/SupportedApps/ownCloud/app.json | 10 + .../www/SupportedApps/ownCloud/ownCloud.php | 7 + .../dashboard/config/www/icons/bookstack.svg | 14 + stacks/dashboard/config/www/icons/dockge.svg | 10 + stacks/dashboard/config/www/icons/dozzle.svg | 142 ++++++++ .../config/www/icons/filebrowser.svg | 25 ++ stacks/dashboard/config/www/icons/ittools.png | Bin 0 -> 6885 bytes .../dashboard/config/www/icons/jellyfin.svg | 15 + .../config/www/icons/nginxproxymanager.png | Bin 0 -> 9101 bytes .../dashboard/config/www/icons/onlyoffice.png | Bin 0 -> 20971 bytes .../dashboard/config/www/icons/owncloud.png | Bin 0 -> 9045 bytes stacks/dashboard/config/www/icons/pihole.svg | 14 + .../dashboard/config/www/icons/uptimekuma.svg | 11 + .../config/www/icons/vaultwarden.png | Bin 0 -> 6766 bytes .../dashboard/config/www/icons/wireguard.png | Bin 0 -> 4827 bytes stacks/dashboard/config/www/index.html | 34 ++ .../config/www/logs/laravel-2026-03-05.log | 307 ++++++++++++++++++ .../dashboard/config/www/searchproviders.yaml | 44 +++ 57 files changed, 1694 insertions(+) create mode 100644 stacks/dashboard/config/www/.env create mode 100644 stacks/dashboard/config/www/SupportedApps/Bookstack/Bookstack.php create mode 100644 stacks/dashboard/config/www/SupportedApps/Bookstack/app.json create mode 100644 stacks/dashboard/config/www/SupportedApps/Bookstack/config.blade.php create mode 100644 stacks/dashboard/config/www/SupportedApps/Bookstack/livestats.blade.php create mode 100644 stacks/dashboard/config/www/SupportedApps/Dockge/Dockge.php create mode 100644 stacks/dashboard/config/www/SupportedApps/Dockge/app.json create mode 100644 stacks/dashboard/config/www/SupportedApps/Dozzle/Dozzle.php create mode 100644 stacks/dashboard/config/www/SupportedApps/Dozzle/app.json create mode 100644 stacks/dashboard/config/www/SupportedApps/FileBrowser/FileBrowser.php create mode 100644 stacks/dashboard/config/www/SupportedApps/FileBrowser/app.json create mode 100644 stacks/dashboard/config/www/SupportedApps/FileBrowser/config.blade.php create mode 100644 stacks/dashboard/config/www/SupportedApps/FileBrowser/livestats.blade.php create mode 100644 stacks/dashboard/config/www/SupportedApps/ITTools/ITTools.php create mode 100644 stacks/dashboard/config/www/SupportedApps/ITTools/app.json create mode 100644 stacks/dashboard/config/www/SupportedApps/JDownloader/JDownloader.php create mode 100644 stacks/dashboard/config/www/SupportedApps/JDownloader/app.json create mode 100644 stacks/dashboard/config/www/SupportedApps/Jellyfin/Jellyfin.php create mode 100644 stacks/dashboard/config/www/SupportedApps/Jellyfin/app.json create mode 100644 stacks/dashboard/config/www/SupportedApps/Jellyfin/config.blade.php create mode 100644 stacks/dashboard/config/www/SupportedApps/Jellyfin/livestats.blade.php create mode 100644 stacks/dashboard/config/www/SupportedApps/NginxProxyManager/NginxProxyManager.php create mode 100644 stacks/dashboard/config/www/SupportedApps/NginxProxyManager/app.json create mode 100644 stacks/dashboard/config/www/SupportedApps/NginxProxyManager/config.blade.php create mode 100644 stacks/dashboard/config/www/SupportedApps/NginxProxyManager/livestats.blade.php create mode 100644 stacks/dashboard/config/www/SupportedApps/OnlyOffice/OnlyOffice.php create mode 100644 stacks/dashboard/config/www/SupportedApps/OnlyOffice/app.json create mode 100644 stacks/dashboard/config/www/SupportedApps/Pihole/Pihole.php create mode 100644 stacks/dashboard/config/www/SupportedApps/Pihole/app.json create mode 100644 stacks/dashboard/config/www/SupportedApps/Pihole/config.blade.php create mode 100644 stacks/dashboard/config/www/SupportedApps/Pihole/livestats.blade.php create mode 100644 stacks/dashboard/config/www/SupportedApps/UptimeKuma/UptimeKuma.php create mode 100644 stacks/dashboard/config/www/SupportedApps/UptimeKuma/app.json create mode 100644 stacks/dashboard/config/www/SupportedApps/UptimeKuma/config.blade.php create mode 100644 stacks/dashboard/config/www/SupportedApps/UptimeKuma/livestats.blade.php create mode 100644 stacks/dashboard/config/www/SupportedApps/Vaultwarden/Vaultwarden.php create mode 100644 stacks/dashboard/config/www/SupportedApps/Vaultwarden/app.json create mode 100644 stacks/dashboard/config/www/SupportedApps/WireGuard/WireGuard.php create mode 100644 stacks/dashboard/config/www/SupportedApps/WireGuard/app.json create mode 100644 stacks/dashboard/config/www/SupportedApps/ownCloud/app.json create mode 100644 stacks/dashboard/config/www/SupportedApps/ownCloud/ownCloud.php create mode 100644 stacks/dashboard/config/www/icons/bookstack.svg create mode 100644 stacks/dashboard/config/www/icons/dockge.svg create mode 100644 stacks/dashboard/config/www/icons/dozzle.svg create mode 100644 stacks/dashboard/config/www/icons/filebrowser.svg create mode 100644 stacks/dashboard/config/www/icons/ittools.png create mode 100644 stacks/dashboard/config/www/icons/jellyfin.svg create mode 100644 stacks/dashboard/config/www/icons/nginxproxymanager.png create mode 100644 stacks/dashboard/config/www/icons/onlyoffice.png create mode 100644 stacks/dashboard/config/www/icons/owncloud.png create mode 100644 stacks/dashboard/config/www/icons/pihole.svg create mode 100644 stacks/dashboard/config/www/icons/uptimekuma.svg create mode 100644 stacks/dashboard/config/www/icons/vaultwarden.png create mode 100644 stacks/dashboard/config/www/icons/wireguard.png create mode 100644 stacks/dashboard/config/www/index.html create mode 100644 stacks/dashboard/config/www/logs/laravel-2026-03-05.log create mode 100644 stacks/dashboard/config/www/searchproviders.yaml diff --git a/stacks/dashboard/config/www/.env b/stacks/dashboard/config/www/.env new file mode 100644 index 0000000..1f6b0b6 --- /dev/null +++ b/stacks/dashboard/config/www/.env @@ -0,0 +1,69 @@ +APP_NAME=Heimdall +APP_ENV=local +APP_KEY=base64:iovIUyf+fbX/1gbhrxHIr/58SLI1F3u/GGVd60r4WpQ= +APP_DEBUG=false +APP_URL=http://localhost + +APP_LOCALE=en +APP_FALLBACK_LOCALE=en +APP_FAKER_LOCALE=en_US +APP_MAINTENANCE_DRIVER=file +APP_MAINTENANCE_STORE=database +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=daily +LOG_STACK=single + +DB_CONNECTION=sqlite +DB_DATABASE=app.sqlite + +#DB_CONNECTION= +#DB_HOST= +#DB_PORT= +#DB_DATABASE= +#DB_USERNAME= +#DB_PASSWORD= + +BROADCAST_CONNECTION=log +CACHE_STORE=file +QUEUE_CONNECTION=sync +SESSION_DRIVER=file +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null +QUEUE_DRIVER=database + +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=smtp +MAIL_HOST=smtp.mailtrap.io +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_FROM_ADDRESS=null +MAIL_FROM_NAME="${APP_NAME}" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= + +PUSHER_APP_ID= +PUSHER_APP_KEY= +PUSHER_APP_SECRET= +PUSHER_APP_CLUSTER=mt1 + +MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" +MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" + +AUTH_ROLES_ENABLE=false +AUTH_ROLES_HEADER="remote-groups" +AUTH_ROLES_HTTP_HEADER="HTTP_REMOTE_GROUPS" +AUTH_ROLES_ADMIN="admin" +AUTH_ROLES_DELIMITER="," + +ALLOW_INTERNAL_REQUESTS=false \ No newline at end of file diff --git a/stacks/dashboard/config/www/SupportedApps/Bookstack/Bookstack.php b/stacks/dashboard/config/www/SupportedApps/Bookstack/Bookstack.php new file mode 100644 index 0000000..5ff07e1 --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/Bookstack/Bookstack.php @@ -0,0 +1,83 @@ +jar = new \GuzzleHttp\Cookie\CookieJar; // Uncomment if cookies need to be set + } + + public function getHeaders() + { + $api_token = $this->config->api_token . ":" . $this->config->api_secret; + + $attrs["headers"] = ["Authorization" => "Token " . $api_token]; + return $attrs; + } + + public function test() + { + $test = parent::appTest( + $this->url("api/shelves?count=0"), + $this->getHeaders() + ); + echo $test->status; + } + + public function livestats() + { + $status = "inactive"; + + $attrs = $this->getHeaders(); + + $data = ["visiblestats" => []]; + + foreach ($this->config->availablestats as $stat) { + if (!isset(self::getAvailableStats()[$stat])) { + continue; + } + + $res = parent::execute( + $this->url("api/" . $stat . "?count=0"), + $attrs + ); + $details = json_decode($res->getBody()); + + $newstat = new \stdClass(); + $newstat->title = self::getAvailableStats()[$stat]; + $newstat->value = isset($details->total) + ? number_format($details->total) + : "N/A"; + + $data["visiblestats"][] = $newstat; + } + + return parent::getLiveStats($status, $data); + } + + public function url($endpoint) + { + $api_url = + rtrim(parent::normaliseurl($this->config->url), "/") . + "/" . + ltrim($endpoint, "/"); + return $api_url; + } + + public static function getAvailableStats() + { + return [ + "shelves" => "Shelves", + "books" => "Books", + "chapters" => "Chapters", + "pages" => "Pages", + ]; + } +} diff --git a/stacks/dashboard/config/www/SupportedApps/Bookstack/app.json b/stacks/dashboard/config/www/SupportedApps/Bookstack/app.json new file mode 100644 index 0000000..605b92d --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/Bookstack/app.json @@ -0,0 +1,10 @@ +{ + "appid": "348c49dd03dddd418929316668d2e67bf2d9ae88", + "name": "Bookstack", + "website": "https://www.bookstackapp.com", + "license": "MIT License", + "description": "BookStack is a simple, self-hosted, easy-to-use platform for organising and storing information.", + "enhanced": true, + "tile_background": "dark", + "icon": "bookstack.svg" +} diff --git a/stacks/dashboard/config/www/SupportedApps/Bookstack/config.blade.php b/stacks/dashboard/config/www/SupportedApps/Bookstack/config.blade.php new file mode 100644 index 0000000..fcb8b6c --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/Bookstack/config.blade.php @@ -0,0 +1,22 @@ +

{{ __('app.apps.config') }} ({{ __('app.optional') }}) @include('items.enable')

+
+
+ + {!! Form::text('config[override_url]', isset($item) ? $item->getConfig()->override_url ?? null : null, ['placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control']) !!} +
+
+ + {!! Form::text('config[api_token]', isset($item) ? $item->getconfig()->api_token : null, ['placeholder' => 'Token ID', 'data-config' => 'api_token', 'class' => 'form-control config-item']) !!} +
+
+ + {!! Form::text('config[api_secret]', isset($item) ? $item->getconfig()->api_secret : null, ['placeholder' => 'Token Secret', 'data-config' => 'api_secret', 'class' => 'form-control config-item']) !!} +
+
+ + {!! Form::select('config[availablestats][]', App\SupportedApps\Bookstack\Bookstack::getAvailableStats(), isset($item) ? $item->getConfig()->availablestats ?? null : null, ['multiple' => 'multiple']) !!} +
+
+ +
+
diff --git a/stacks/dashboard/config/www/SupportedApps/Bookstack/livestats.blade.php b/stacks/dashboard/config/www/SupportedApps/Bookstack/livestats.blade.php new file mode 100644 index 0000000..cb9431a --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/Bookstack/livestats.blade.php @@ -0,0 +1,8 @@ +
    + @foreach ($visiblestats as $stat) +
  • + {!! $stat->title !!} + {!! $stat->value !!} +
  • + @endforeach +
diff --git a/stacks/dashboard/config/www/SupportedApps/Dockge/Dockge.php b/stacks/dashboard/config/www/SupportedApps/Dockge/Dockge.php new file mode 100644 index 0000000..6d931b7 --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/Dockge/Dockge.php @@ -0,0 +1,7 @@ +jar = new \GuzzleHttp\Cookie\CookieJar; // Uncomment if cookies need to be set + } + + private function getToken() + { + $username = $this->config->username; + $password = $this->config->password; + $authHeader = $this->config->authHeader; + + $attrs = [ + "headers" => [ + "Content-Type" => "application/json" + ], + "body" => json_encode([ + "username" => $username, + "password" => $password + ]) + ]; + if ($authHeader !== null) { + $attrs["headers"][$authHeader] = $username; + } + + $res = parent::execute($this->url("api/login"), $attrs, null, "POST"); + + switch ($res->getStatusCode()) { + case 200: + return $res->getBody()->getContents(); + case 403: + throw new \Exception("Invalid username/password"); + default: + throw new \Exception("Could not connect to FileBrowser"); + } + } + + private function formatBytes($bytes, $precision = 2) { + $units = array('B', 'KB', 'MB', 'GB', 'TB'); + $bytes = max($bytes, 0); + $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); + $pow = min($pow, count($units) - 1); + + $bytes /= pow(1024, $pow); + + $value = round($bytes, $precision); + $unit = $units[$pow]; + + return [ + "value" => $value, + "unit" => $unit + ]; + } + + public function test() + { + try { + $token = $this->getToken(); + echo "Successfully communicated with the API"; + } catch (Exception $err) { + echo $err->getMessage(); + } + } + + public function livestats() + { + $status = "inactive"; + + $token = $this->getToken(); + $attrs = [ + "headers" => [ + "X-AUTH" => $token + ], + ]; + $res = parent::execute($this->url("api/usage"), $attrs); + $details = json_decode($res->getBody()); + + if ($details != null) { + $data["used"] = $this->formatBytes($details->used); + $data["total"] = $this->formatBytes($details->total); + } + return parent::getLiveStats($status, $data); + } + + public function url($endpoint) + { + $api_url = parent::normaliseurl($this->config->url) . $endpoint; + + return $api_url; + } +} diff --git a/stacks/dashboard/config/www/SupportedApps/FileBrowser/app.json b/stacks/dashboard/config/www/SupportedApps/FileBrowser/app.json new file mode 100644 index 0000000..0e412a6 --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/FileBrowser/app.json @@ -0,0 +1,10 @@ +{ + "appid": "ba05dd8e070851895ee6184eb9778cfa0753a490", + "name": "FileBrowser", + "website": "https://github.com/filebrowser/filebrowser", + "license": "Apache License 2.0", + "description": "filebrowser provides a file managing interface within a specified directory and it can be used to upload, delete, preview, rename and edit your files. It allows the creation of multiple users and each user can have its own directory. It can be used as a standalone app or as a middleware.", + "enhanced": true, + "tile_background": "dark", + "icon": "filebrowser.svg" +} diff --git a/stacks/dashboard/config/www/SupportedApps/FileBrowser/config.blade.php b/stacks/dashboard/config/www/SupportedApps/FileBrowser/config.blade.php new file mode 100644 index 0000000..6436ca8 --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/FileBrowser/config.blade.php @@ -0,0 +1,29 @@ +

{{ __('app.apps.config') }} ({{ __('app.optional') }}) @include('items.enable')

+
+
+
+ + {!! Form::text('config[override_url]', isset($item) ? $item->getconfig()->override_url : null, ['placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control']) !!} +
+
+
+
+ + {!! Form::text('config[username]', isset($item) ? $item->getconfig()->username : null, ['placeholder' => __('app.apps.username'), 'data-config' => 'username', 'class' => 'form-control config-item']) !!} +
+
+ + {!! Form::input('password', 'config[password]', '', ['placeholder' => __('app.apps.password'), 'data-config' => 'password', 'class' => 'form-control config-item']) !!} +
+
+
+
+ + {!! Form::text('config[authHeader]', isset($item) ? $item->getconfig()->authHeader : null, ['placeholder' => 'X-My-Header', 'data-config' => 'authHeader', 'class' => 'form-control config-item']) !!} + Also fill {{ __('app.apps.username') }} +
+
+
+ +
+
diff --git a/stacks/dashboard/config/www/SupportedApps/FileBrowser/livestats.blade.php b/stacks/dashboard/config/www/SupportedApps/FileBrowser/livestats.blade.php new file mode 100644 index 0000000..93f35f6 --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/FileBrowser/livestats.blade.php @@ -0,0 +1,10 @@ +
    +
  • + Used + {!! $used["value"] !!}{!! $used["unit"] !!} +
  • +
  • + Total + {!! $total["value"] !!}{!! $total["unit"] !!} +
  • +
diff --git a/stacks/dashboard/config/www/SupportedApps/ITTools/ITTools.php b/stacks/dashboard/config/www/SupportedApps/ITTools/ITTools.php new file mode 100644 index 0000000..5836b43 --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/ITTools/ITTools.php @@ -0,0 +1,7 @@ +url("System/Info"), + $this->getAttrs() + ); + echo $test->status; + } + + public function livestats() + { + $status = "inactive"; + $res = parent::execute($this->url("Items/Counts"), $this->getAttrs()); + $result = json_decode($res->getBody()); + $details = ["visiblestats" => []]; + foreach ($this->config->availablestats as $stat) { + $newstat = new \stdClass(); + $newstat->title = self::getAvailableStats()[$stat]; + $newstat->value = $result->{$stat}; + $details["visiblestats"][] = $newstat; + } + return parent::getLiveStats($status, $details); + } + public function url($endpoint) + { + $api_url = parent::normaliseurl($this->config->url) . $endpoint; + return $api_url; + } + + private function getAttrs() + { + $authorizationHeader = "MediaBrowser " . + "Token=\"" . urlencode($this->config->password) . "\", " . + "Client=\"Heimdall\""; + return [ + "headers" => [ + "Authorization" => $authorizationHeader, + ], + ]; + } + + public static function getAvailableStats() + { + return [ + "MovieCount" => "Movies", + "SeriesCount" => "Series", + "EpisodeCount" => "Episodes", + "ArtistCount" => "Artists", + "ProgramCount" => "Programs", + "TrailerCount" => "Trailers", + "SongCount" => "Songs", + "AlbumCount" => "Albums", + "MusicVideoCount" => "MusicVideos", + "BoxSetCount" => "BoxSets", + "BookCount" => "Books", + "ItemCount" => "Items", + ]; + } +} diff --git a/stacks/dashboard/config/www/SupportedApps/Jellyfin/app.json b/stacks/dashboard/config/www/SupportedApps/Jellyfin/app.json new file mode 100644 index 0000000..2460041 --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/Jellyfin/app.json @@ -0,0 +1,10 @@ +{ + "appid": "3e0a7f109bd760b9474c78cb652e8c3e82669226", + "name": "Jellyfin", + "website": "https://jellyfin.github.io", + "license": "GNU General Public License v2.0 only", + "description": "Jellyfin is the Free Software Media System that puts you in control of managing and streaming your media. There are no strings attached, no premium licenses or features, and no hidden agendas.", + "enhanced": true, + "tile_background": "dark", + "icon": "jellyfin.svg" +} diff --git a/stacks/dashboard/config/www/SupportedApps/Jellyfin/config.blade.php b/stacks/dashboard/config/www/SupportedApps/Jellyfin/config.blade.php new file mode 100644 index 0000000..faa3632 --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/Jellyfin/config.blade.php @@ -0,0 +1,18 @@ +

{{ __('app.apps.config') }} ({{ __('app.optional') }}) @include('items.enable')

+
+
+ + {!! Form::text('config[override_url]', isset($item) ? $item->getconfig()->override_url : null, ['placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control']) !!} +
+
+ + {!! Form::input('password', 'config[password]', '', ['placeholder' => __('app.apps.password'), 'data-config' => 'password', 'class' => 'form-control config-item']) !!} +
+
+ + {!! Form::select('config[availablestats][]', App\SupportedApps\Jellyfin\Jellyfin::getAvailableStats(), isset($item) ? $item->getConfig()->availablestats ?? null : null, ['multiple' => 'multiple']) !!} +
+
+ +
+
diff --git a/stacks/dashboard/config/www/SupportedApps/Jellyfin/livestats.blade.php b/stacks/dashboard/config/www/SupportedApps/Jellyfin/livestats.blade.php new file mode 100644 index 0000000..cb9431a --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/Jellyfin/livestats.blade.php @@ -0,0 +1,8 @@ +
    + @foreach ($visiblestats as $stat) +
  • + {!! $stat->title !!} + {!! $stat->value !!} +
  • + @endforeach +
diff --git a/stacks/dashboard/config/www/SupportedApps/NginxProxyManager/NginxProxyManager.php b/stacks/dashboard/config/www/SupportedApps/NginxProxyManager/NginxProxyManager.php new file mode 100644 index 0000000..026e30b --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/NginxProxyManager/NginxProxyManager.php @@ -0,0 +1,61 @@ +jar = new \GuzzleHttp\Cookie\CookieJar; // Uncomment if cookies need to be set + } + + public function test() + { + $test = parent::appTest($this->url("api")); + echo $test->status; + } + + public function livestats() + { + $status = "inactive"; + $auth_attrs = [ + "headers" => [ + "Accept" => "application/json", + "Content-Type" => "application/json", + ], + "body" => json_encode([ + "identity" => $this->config->email, + "secret" => $this->config->password, + ]), + ]; + $auth_res = parent::execute( + $this->url("api/tokens"), + $auth_attrs, + null, + "POST" + ); + $auth_data = json_decode($auth_res->getBody(), true); + $token = $auth_data["token"]; + + $attrs = [ + "headers" => [ + "Accept" => "application/json", + "Authorization" => "Bearer " . $token, + ], + ]; + $res = parent::execute($this->url("api/reports/hosts"), $attrs); + $data = json_decode($res->getBody(), true); + + return parent::getLiveStats($status, $data); + } + public function url($endpoint) + { + $api_url = parent::normaliseurl($this->config->url) . $endpoint; + return $api_url; + } +} diff --git a/stacks/dashboard/config/www/SupportedApps/NginxProxyManager/app.json b/stacks/dashboard/config/www/SupportedApps/NginxProxyManager/app.json new file mode 100644 index 0000000..87e8c44 --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/NginxProxyManager/app.json @@ -0,0 +1,10 @@ +{ + "appid": "cbfad988a16a9fbcc1812bc206afcc1f73dd36de", + "name": "Nginx Proxy Manager", + "website": "https://nginxproxymanager.jc21.com", + "license": "MIT License", + "description": "This project comes as a pre-built docker image that enables you to easily forward to your websites running at home or otherwise, including free SSL, without having to know too much about Nginx or Letsencrypt.", + "enhanced": true, + "tile_background": "light", + "icon": "nginxproxymanager.png" +} diff --git a/stacks/dashboard/config/www/SupportedApps/NginxProxyManager/config.blade.php b/stacks/dashboard/config/www/SupportedApps/NginxProxyManager/config.blade.php new file mode 100644 index 0000000..0173dbc --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/NginxProxyManager/config.blade.php @@ -0,0 +1,18 @@ +

{{ __('app.apps.config') }} ({{ __('app.optional') }}) @include('items.enable')

+
+
+ + {!! Form::text('config[override_url]', isset($item) ? $item->getconfig()->override_url : null, ['placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control']) !!} +
+
+ + {!! Form::text('config[email]', isset($item) ? $item->getconfig()->email : null, ['placeholder' => __('Email address'), 'data-config' => 'email', 'class' => 'form-control config-item']) !!} +
+
+ + {!! Form::input('password', 'config[password]', '', ['placeholder' => __('app.apps.password'), 'data-config' => 'password', 'class' => 'form-control config-item']) !!} +
+
+ +
+
diff --git a/stacks/dashboard/config/www/SupportedApps/NginxProxyManager/livestats.blade.php b/stacks/dashboard/config/www/SupportedApps/NginxProxyManager/livestats.blade.php new file mode 100644 index 0000000..8536006 --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/NginxProxyManager/livestats.blade.php @@ -0,0 +1,10 @@ +
    +
  • + Proxy Hosts + {!! $proxy !!} +
  • +
  • + Redirection Hosts + {!! $redirection !!} +
  • +
diff --git a/stacks/dashboard/config/www/SupportedApps/OnlyOffice/OnlyOffice.php b/stacks/dashboard/config/www/SupportedApps/OnlyOffice/OnlyOffice.php new file mode 100644 index 0000000..b9c6f39 --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/OnlyOffice/OnlyOffice.php @@ -0,0 +1,7 @@ +jar = new \GuzzleHttp\Cookie\CookieJar(); // Uncomment if cookies need to be set + } + + public function test() + { + $version = $this->config->version; + + if ($version == 5) { + $test = parent::appTest($this->url("api.php?summaryRaw")); + echo $test->status; + } + if ($version == 6) { + $test = $this->getInfo(); + if ($test["valid"]) { + echo "Successfully communicated with the API"; + } else { + echo "Error while communicating with the API: " . $test["message"]; + } + } + } + public function livestats() + { + $data = []; + $status = "inactive"; + $version = $this->config->version; + + if ($version == 5) { + $res = parent::execute($this->url("api.php?summaryRaw")); + $details = json_decode($res->getBody()); + if ($details) { + $data["ads_blocked"] = number_format( + $details->ads_blocked_today + ); + $data["ads_percentage"] = number_format( + $details->ads_percentage_today, + 1 + ); + + $status = "active"; + } + } + + if ($version == 6) { + $results = $this->getInfo(); + + if ($results["valid"]) { + $data["ads_blocked"] = $results["queries"]; + $data["ads_percentage"] = $results["percent"]; + + $status = "active"; + } + } + return parent::getLiveStats($status, $data); + } + + public function url($endpoint) + { + $version = $this->config->version; + if ($version == 5) { + $apikey = $this->config->apikey; + $api_url = parent::normaliseurl($this->config->url) . $endpoint; + + if ($apikey) { + $api_url .= "&auth=" . $apikey; + } + } + if ($version == 6) { + $api_url = parent::normaliseurl($this->config->url) . $endpoint; + } + return $api_url; + } + + public function getInfo() + { + $ignoreTls = $this->getConfigValue("ignore_tls", false); + if ($ignoreTls) { + $attrs = [ + "body" => json_encode(['password' => $this->config->apikey]), + "cookies" => $this->jar, + "verify" => false, + "headers" => [ + "Content-Type" => "application/json", + "Accept" => "application/json", + ], + ]; + $attrsid["verify"] = false; + } else { + $attrs = [ + "body" => json_encode(['password' => $this->config->apikey]), + "cookies" => $this->jar, + "headers" => [ + "Content-Type" => "application/json", + "Accept" => "application/json", + ], + ]; + } + + // Create session and retrieve data + $response = parent::execute($this->url("api/auth"), $attrs, null, "POST"); + $auth = json_decode($response->getBody()); + + if (!$auth->session->valid) { + $data = [ + 'valid' => false, + 'validity' => -1, + 'message' => $auth->session->message, + 'queries' => 0, + 'percent' => 0 + ]; + return $data; + } + + if ($ignoreTls) { + $attrsid = [ + "body" => json_encode(['sid' => $auth->session->sid]), + "cookies" => $this->jar, + "verify" => false, + "headers" => [ + "Content-Type" => "application/json", + "Accept" => "application/json", + ], + ]; + } else { + $attrsid = [ + "body" => json_encode(['sid' => $auth->session->sid]), + "cookies" => $this->jar, + "headers" => [ + "Content-Type" => "application/json", + "Accept" => "application/json", + ], + ]; + } + + // Get queries data + $responsesummary = parent::execute($this->url("api/stats/summary"), $attrsid, null, "GET"); + $datasummary = json_decode($responsesummary->getBody()); + + // After retrieving the data the session is closed to declutter + parent::execute($this->url("api/auth"), $attrsid, null, "DELETE"); + + // Extract data from the response + $valid = $auth->session->valid; + $validity = $auth->session->validity; + $message = $auth->session->message; + + if (!$auth->session->valid) { + $queriesblocked = 0; + $percentblocked = 0; + } else { + $queriesblocked = $datasummary->queries->blocked; + $percentblocked = round($datasummary->queries->percent_blocked, 2); + } + + $data = [ + 'valid' => $valid, + 'validity' => $validity, + 'message' => $message, + 'queries' => $queriesblocked, + 'percent' => $percentblocked + ]; + return $data; + } + public function getConfigValue($key, $default = null) + { + return isset($this->config) && isset($this->config->$key) + ? $this->config->$key + : $default; + } +} diff --git a/stacks/dashboard/config/www/SupportedApps/Pihole/app.json b/stacks/dashboard/config/www/SupportedApps/Pihole/app.json new file mode 100644 index 0000000..5490455 --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/Pihole/app.json @@ -0,0 +1,10 @@ +{ + "appid": "b89920409bdce40e08ba1023480b0546061cd577", + "name": "Pi-hole", + "website": "https://pi-hole.net", + "license": "European Union Public License 1.2", + "description": "Pi-hole is a Linux network-level advertisement and internet tracker blocking application which acts as a DNS sinkhole, intended for use on a private network.", + "enhanced": true, + "tile_background": "dark", + "icon": "pihole.svg" +} diff --git a/stacks/dashboard/config/www/SupportedApps/Pihole/config.blade.php b/stacks/dashboard/config/www/SupportedApps/Pihole/config.blade.php new file mode 100644 index 0000000..3d8eff4 --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/Pihole/config.blade.php @@ -0,0 +1,43 @@ +

{{ __('app.apps.config') }} ({{ __('app.optional') }}) @include('items.enable')

+
+ +
+ + {!! Form::text('config[override_url]', isset($item) ? $item->getconfig()->override_url : null, ['placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control']) !!} +
+
+ + {!! Form::text('config[apikey]', isset($item) && property_exists($item->getconfig(), 'apikey') ? $item->getconfig()->apikey : null, ['placeholder' => __('app.apps.apikey'), 'data-config' => 'apikey', 'class' => 'form-control config-item']) !!} +
+
+
+ + {!! Form::select( + 'config[version]', + ['5' => 'v5', '6' => 'v6'], + isset($item) ? $item->getconfig()->version : null, + ['data-config' => 'version', 'class' => 'form-control config-item'], + ) !!} +
+
+
+ +
+ {!! Form::hidden('config[ignore_tls]', 0, ['class' => 'config-item', 'data-config' => 'ignore_tls']) !!} + +
+
+
+ +
+
diff --git a/stacks/dashboard/config/www/SupportedApps/Pihole/livestats.blade.php b/stacks/dashboard/config/www/SupportedApps/Pihole/livestats.blade.php new file mode 100644 index 0000000..d905f0b --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/Pihole/livestats.blade.php @@ -0,0 +1,10 @@ +
    +
  • + Queries
    Blocked
    + {!! $ads_blocked !!} +
  • +
  • + Percent
    Blocked
    + {!! $ads_percentage !!} +
  • +
diff --git a/stacks/dashboard/config/www/SupportedApps/UptimeKuma/UptimeKuma.php b/stacks/dashboard/config/www/SupportedApps/UptimeKuma/UptimeKuma.php new file mode 100644 index 0000000..6e35524 --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/UptimeKuma/UptimeKuma.php @@ -0,0 +1,95 @@ + "down", + 1 => "up", + 2 => "pending", + 3 => "maintenance", + ]; + + public function __construct() + { + } + + private function getAttrs() + { + if (empty($this->config->apikey)) { + return []; + } + + $basicAuthValue = base64_encode(":" . $this->config->apikey); + + return [ + "headers" => [ + "Authorization" => "Basic " . $basicAuthValue, + ] + ]; + } + + public function test() + { + + $test = parent::appTest($this->url("metrics"), $this->getAttrs()); + echo $test->status; + } + + public function livestats() + { + $status = "inactive"; + + $response = parent::execute($this->url("metrics"), $this->getAttrs()); + $body = $response->getBody(); + + $lines = explode("\n", $body); + + $data = [ + "up" => 0, + "down" => 0, + "pending" => 0, + "maintenance" => 0, + "unknown" => 0, + ]; + + foreach ($lines as $line) { + if (strlen($line) === 0 || strpos($line, '#') === 0) { + // If the line is empty or is a comment we can skip it + continue; + } + + if (strpos($line, 'monitor_status') !== 0) { + // If the line is a metric but not a monitor we can ignore it + continue; + } + + // We only really care about the state, which is the integer at the end + // of the line. + // + // This can be 0 (Down), 1 (Up), 2 (Pending), 3 (Maintenance) + // + // We only care about the down or up but let's translate all of them. + $state = intval(substr($line, strrpos($line, ' '))); + + $data[$this->mapping[$state] ?? 'unknown']++; + } + + if ($data["down"] > 0) { + $status = "active"; + } + + return parent::getLiveStats($status, $data); + } + + public function url($endpoint) + { + $api_url = parent::normaliseurl($this->config->url) . + $endpoint; + + return $api_url; + } +} diff --git a/stacks/dashboard/config/www/SupportedApps/UptimeKuma/app.json b/stacks/dashboard/config/www/SupportedApps/UptimeKuma/app.json new file mode 100644 index 0000000..2ebffba --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/UptimeKuma/app.json @@ -0,0 +1,10 @@ +{ + "appid": "366c6646eedab83cc4b349f198424d2291cbfa76", + "name": "Uptime Kuma", + "website": "https://uptime.kuma.pet", + "license": "MIT License", + "description": "It is a self-hosted monitoring tool like \"Uptime Robot\".", + "enhanced": true, + "tile_background": "dark", + "icon": "uptimekuma.svg" +} diff --git a/stacks/dashboard/config/www/SupportedApps/UptimeKuma/config.blade.php b/stacks/dashboard/config/www/SupportedApps/UptimeKuma/config.blade.php new file mode 100644 index 0000000..aa383f0 --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/UptimeKuma/config.blade.php @@ -0,0 +1,32 @@ +

{{ __('app.apps.config') }} ({{ __('app.optional') }}) @include('items.enable')

+
+ +
+ + {!! Form::text('config[override_url]', isset($item) ? $item->getconfig()->override_url : null, ['placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control']) !!} +
+
+ + {!! Form::text('config[apikey]', isset($item) ? ($item->getconfig()->apikey ?? null) : null, ['placeholder' => __('app.apps.apikey'), 'data-config' => 'apikey', 'class' => 'form-control config-item']) !!} +
+
+ +
+ {!! Form::hidden('config[ignore_tls]', 0, ['class' => 'config-item', 'data-config' => 'ignore_tls']) !!} + +
+
+
+ +
+
diff --git a/stacks/dashboard/config/www/SupportedApps/UptimeKuma/livestats.blade.php b/stacks/dashboard/config/www/SupportedApps/UptimeKuma/livestats.blade.php new file mode 100644 index 0000000..109206a --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/UptimeKuma/livestats.blade.php @@ -0,0 +1,10 @@ +
    +
  • + Up + {!! $up ?? 0 !!} +
  • +
  • + Down + {!! $down ?? 0 !!} +
  • +
diff --git a/stacks/dashboard/config/www/SupportedApps/Vaultwarden/Vaultwarden.php b/stacks/dashboard/config/www/SupportedApps/Vaultwarden/Vaultwarden.php new file mode 100644 index 0000000..ffb7a58 --- /dev/null +++ b/stacks/dashboard/config/www/SupportedApps/Vaultwarden/Vaultwarden.php @@ -0,0 +1,7 @@ + + + + + + + + + + + + + + diff --git a/stacks/dashboard/config/www/icons/dockge.svg b/stacks/dashboard/config/www/icons/dockge.svg new file mode 100644 index 0000000..c3d6be2 --- /dev/null +++ b/stacks/dashboard/config/www/icons/dockge.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/stacks/dashboard/config/www/icons/dozzle.svg b/stacks/dashboard/config/www/icons/dozzle.svg new file mode 100644 index 0000000..7712588 --- /dev/null +++ b/stacks/dashboard/config/www/icons/dozzle.svg @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/stacks/dashboard/config/www/icons/filebrowser.svg b/stacks/dashboard/config/www/icons/filebrowser.svg new file mode 100644 index 0000000..95b6ac3 --- /dev/null +++ b/stacks/dashboard/config/www/icons/filebrowser.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/stacks/dashboard/config/www/icons/ittools.png b/stacks/dashboard/config/www/icons/ittools.png new file mode 100644 index 0000000000000000000000000000000000000000..465b7c498006d5fae0f59887951b4f9ad0e9afff GIT binary patch literal 6885 zcmWky19Tl-7k!OwHg;p%wr$(^V!r6bYSh?vW224Hq_K@Qd2wFQJy#RhJOzgq2<9qVa&mI*jtHIh2#v-dvQiUPR#uvF3mY37qs|z2clR%q)@;@G0`+dD zT_9+rDmN!DGld>SMMc!*=Fm)$1~2JmU*QH%=e{)3Qe#O;$uCtl;>|wv6;|xk4ubXW z4h{}{b*_w6Hk>t%ELC>UOu+_sW@ct$Vq%gKqc4>gr^r#!Qtq;tk&Xb#C;P zR(!RtLUkU(4Ibi+-YTuZ{Pk`b85uIoel9L9&?}0qfwi@@l1)B^g@w?kKoOLcl{ssj zw6(P*8olM3{KT5PJv}{@+k&A`iHV6ZF)_wnu|}QI4Gj&%C5BKRSXfy2`1nXljY-Rl zb#-;QYMe|=OacM|lv;wIrtCEiP_e`>u6^{!B4 zb8~ar3d^XdC}<&6vSxd@e6znGte~L4UE@quZf0$5 zt)`|XAtB-Anzveuhtf#udlD!7RFZXAXM+c&(F`r#l^|V3B{qS zuoMy!f=1|4QBe`90?Jva&Yis6)WgF=qR|KHKTEY86jokd-r3n1${A{`lamt^8R`IZ zPqVYL?d|P(czB?&gvADE`5Lsew2X|5P*Da31~M`-&;}qXF{G=sgjRj~_AM|lP*6}1 z`Z(Mo9a2(KCg{okvw6^Su&&$hGQBl$P9s$Ub#4glE&QpVC>gebg zeUCOYG&F6G)$fRcCgbDdLzhqjP`1#ngYt();$lNeN=lqU9n1nPydph7cqGdI%L=HX_eCdbFc z_)%9^Sdb9vY^SEkMn^+}_y&O-ug=r{1zQd}LYRuTlv@YMieTbtdrm zYjQBiwFSLKnROkK6m`C_)BUG&LmX({{3!wI!?Kz{N3eB%ipN!oA;#A0X}++x;B^=; z_(Mm3xN45n;jf*X1>H>OF3N_}mhlMFUxJW7J8fglD9XW2h@xa>KR?SbIGp<{TA}}3 zvzMr=nc_Me zmnVqbWEB)=;>j1$VuY)auSbPunnkK{@ktMo*8KA43JSuV{B;1fm&8P*M^DZhIWFN; z7$_me!{Luj+uQ81{8Qr&81x!3|Yn?PaY#oRG^Ma~bh%p%~Lk9tD8 zsinK9j*q3EUHj5#8ixCGPRvxLn>U;OZu>GhXz~+|vt^E#{7Xq+3(bGYBcN2P?3iL8 zO|pG+JF8#@RzkrN$)FH0jS?2np>>sf zR=KFjz>ty29D8Q;n*CrTMNCAYm?PeswRMa(y*K*H&iy}C)akUVU*ZJ=3(iP`(r zGgp7X4?BTIO~^n>)D&q`FHLx83~%_q;S8x%0P-g;Vz>r z9$7e5cqASxBH*H3Vq(>Uh;8A8w`kJ5Lq(<>=czm7!F z)FHzN_Kk4xX(?&RC@C^>vQ_9~snl_B1R{72qXIM9m5^^q$){!7AOM)az8MZJ8&q{8 zL}9-Ir$I5^74e+VwAp0U?Vyd$y1LneMtkMNbce`dH#|JKSk#o0z!!o=bPYM{j@~AQ z3Xxb2t`lA)7e5<)L)*=jvCK;iQ|+hokeS5l#~I;IR5PrGMC8bu8@?x~kj0Oqavs4L z`4eBHeYrGlv`K>cwh#bc$)Qf3hwWN)*sa%UlQx_tV5n$&d)vy-Dl#5AMi5o!tR<0I zSWqFvM-vTJLlTfm#X&QL!a<$Aot?X)PcJSy%^qq`vwts6F$ErP>`wa&x^p{2hBFl% z2d9UYxm=lHn@BJ7CQAUGR5@RMt!P;@&;q@rV7pefN@HioWb0Rdo;3MCNj=seU4=elc^Wf=gC7-`_Flk6=L>Az?$&Yya zUjDkb{-7W6pXBJ^L$ajkpNCO(L)LECm^V48vWxg`?qg^$0 zkvoSK$7wq{Z)@^~Q8QEj;ENd+pkc(A2!wdwk5^kdXIY*HN-gqkuJAIK$bSDbO_~=7 zPa@oaK>j5)gPI0&E_^Vzy>0a&1?)4ui2JL0teNITX55e=ob>J!Ju3A^MJo6?Pwkp8 zA<$IWAjVBO?0)KZc4TA>7aMrsn#u#Lfs=iw5da`Urz}kUG`#@^AbWbiBNHv!5Ud4EPAeQ`9x#|0uibU@H zO3L|}8jwVIF^8~U&_?H|pK-r`k>)=WlZ|nd%>VR%YlD`n>n&3!8K(n(V0TxF!+fu_ z981OYgYiUX^eitglBPej64F14qYmd+k?XsV(#WAbxd%p0Lbaz3PV>>fLJN&F{wMFRf`(cwxkN%SV{J( zQe}B|kt_1RslV;PTtLZ#oP zm1AW&cpURdf1{UrZv|FHXq4KKU61|ZIYmy?816{gBZOCC(xJ~!&)D-bR}!MupLpZY#3k|kkp`ilfvKg)4q&~!U;gh&ng)7K;}A6!+GBI z>X5u%j_c%6fY5C=Cp2T5VAjcJ!)Eu^?HYdL3B+!`(59BiV)!H>-_j=fNeDjFZy{Y{ z27k!5jzC$aEDqF-O#~a==y5+u;Uyg^NK6a!70@=%7yLCQZ-*v$>2=*bh`4FSf&bB# zf(rN3K)sWO2tFM=O%E1$!ZWxL&t0pRD@T$TQ!TnNQ9UG5~{ctZZ%(Ay%gLF~jU^!%9 z^aTYdXq*)<$be?s@g2?f&8m8_wUz-qxAph$->bhaT}ZZHXhhpK|Iw2UGC_PP-d}rm zgOsYjlwi-ps1X2Um)4iPG|G72mMMk_1-lFMs#{;#6kWkWUv^7L3R& z-9g3=2#7q#C0b1>DKA?T?7P2Zl`L1j64r?8ePM|cB>P6%mZ!{8C1&=L0K*-l>3C!r z=c)wc4>Tp=4F^rz+zh)iZOfFR&M2(FUcNKt*C-FJH>npM`86HeiOs&96@vOo3TLvO58PTA%ZfiX7IKlOLR_^>3}ru4}x{ z)t#ByuPud02>jlAf3Mba)bXYDnrqcHh+XxE;)fKLJ|Xv=Wqm)WD{>2Zh;L8Ob7>|H z6@KNa)%HIu2LC6(UW5zbbm?~>ig87k z#kyjDZ^(0{r`E8A#NlL;yDCc`C?Zec##@?orCBAysR%jOLcn-w(+iN$!tMUbuvq%7-G`9DJ14%4+pa@rZSe4&`&n!$Db-G9He$LIZPZ+@~- zj59dFj=ww#Je*6FpW$2{HQG$t|NbuEvKE@cH@oJ{P(9U%&Zv9eC+V(!ZMnl^j6?my z6cI1wA>9e&XnWB?BM-x!v~4zpSP5bJpuK=In>SBzB*I%Fso|aTpnTIA1f+=hZf3Ym?SMo9Qd0FNJzv@|LC=CNh;*WO2h!=FGG*XZ(je zdyQsKc$W&qsGMf{?D=n`qPYV7!4@XDjaJ?U=W>lETrBe~&4!}oOs!qqdjjV8%p0UT zp59Vp3bLnm*H$huIu-UPfmS}&TH4duXtiFW4~01bImh3Yeyc%RCi$0rU?HAuc?m+r z${lSeeO)oO7F8aid$}P;Y6AlyJ-FRo!>*5Rbs`T!(Zgqx%v2~&S>2lQyftTq19&3q zg~x{J_XZ%~KL1)8bLXtA>)xa!Y*fS*E+q6>lj5vgYaqyoAu;@sOlNBrFZ7coeiw0qWNkBco_)a;#` z4BtF)Ps1wN6P3=v70lr=BsUN#FC%fmwQ&9vZS;HeN$lx>Kb4M60?Z$8tO_zp8#j!- z{o9y^Cz32Pmd}HC<6%9PMwPB--O0A##*n0RRnPdD7bK8KJ;KPb014SL+Vt5Vc%MBh z#5Zi)_+QvBO~2j9qWKVC_pxOX;Z* zNNQkcKAWYm>>};|qBc-Co>M}am{5eDV*oB=l?5sfCYfA5w)!Id+&|=s!etFWj~a6( z%$@LX3nRaN+HH~4L*kFN9+}$oRygkICW2{sqSgI{Bu>429)0+n&$$0}vlkI}fP^j| zpB3*i+xoEEQ54%D1)Nn8)mBy!>HI;^6s!tp67wGXo=*8mLpxT2!wBrxQumKfg3Afa zIEohyCxZ@oSn(Co>wXsDvK0+Z3G7a#AJz;4+T4!ncPZ@d_z_;WJ!En}?pD(+A>$yF zN~xi17h2iD z)>s?q9H=@oq_aCVx+PJerzz&BBKstkIAPbLFjKvyM-eKd)xEuS;Os3(+iA4BQPkvO`a(e!>+G$oNuV}x z8rJ4;7=)yH!o!C7M6K5C)^OX|O;9`ARuP>&Cla{y&1ST+ZKl+y&>PRAd5o8HO7)fa z`gjIwaQU~E?;GE1IAXhK+=sijr{HySW@DL)n;ugIx%7X3t<&&ZhWYho85!PB;p}0| z=_9q=y4WS)2}miccyMx}OfGujK6Br91aLjR z$(`5Zaey1o^@!D>p4$JoJdyg84CmG%)9`2TdG8W8VUq=*kbYvp^6g2t--0Da>+zZVj@6)Pk1z9BRuMRM znl27SHkt*Z<^opYS<;FI3Q&SfdX|5|d?7z}u#>NUI;$|ARXARt z=_Mt1!LZsHKi!Oob%QB{wdLuXv+^6 zwttMx=^DAPPCR{q8}i2RK6I`nIIyBK2Dhz$kil8Nd9xV{kRMnLG;)9Gtnr^4U-;#? z0w2aU_H*Onc)3qE7UWdT{I{)olk(;c@?1N9Nhl@pIE6*u(-bQZu$ESfmlk=NMMGQ! zV$VnNT-^+QcA=Y$uqHp=n)ExX zZRFpIPNSF~VrS6q5vm^hA)tRAloU>eCD+%Jra;1}39bc-sF~m!ROS3ZK`Bw@#$cM% zqoS}BTOy>Pqv&pfYeEM{yod0U`EiK;og5Pf8k)iF`>g!^OMVXxeI+LOq3Mo4(6U%! z|g%x*2T;KzyV{9 UXo{gUbOrz@%BsmUN?8E^2je{lPXGV_ literal 0 HcmV?d00001 diff --git a/stacks/dashboard/config/www/icons/jellyfin.svg b/stacks/dashboard/config/www/icons/jellyfin.svg new file mode 100644 index 0000000..6c8cbfa --- /dev/null +++ b/stacks/dashboard/config/www/icons/jellyfin.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/stacks/dashboard/config/www/icons/nginxproxymanager.png b/stacks/dashboard/config/www/icons/nginxproxymanager.png new file mode 100644 index 0000000000000000000000000000000000000000..7c5b28a0d654fdffd915a09fa1fca79d3a12ad12 GIT binary patch literal 9101 zcmX9^2Q-`C`)^BW&9=U^Ls5IgEMip^EiFYAwQAI;O%O3sYSl<=ZLOLyiW;>NqSUI| zd#|X(jEEvJ{(k3w&VAqaKKDN7c|M=dx%a;J+;cy^FwkYBfV(7};{>D_5?ay?FLg`_ddTe7&cr9Hsrml;43|2<(?l&D6;%QjFd-mOCz!-LjCG zlZx24mxlm9d;lT#&E+$c(t0&gyZG*pswX8G3KQn17}%E5ztd4__CeoD7)_>ODP7P z&MVg1Bmb9CHKRLF#QQlF|XFw)pN(nKel=xmTZd=}RKrVGfPG(zPXHZyT>i+As zCsG6@rT>R_oM}6#m_~dn`%x&Miql|C2uvvfUJ9}qB(?o8v6WZ+Vpw!QIep+k+GX|> z^~^ke|Ef17sXAFG^MdnX(aSg(xzytn={+T-4Vkc$BB@IZdlNFHMtPU0oCLithD0ug zL^~uU{uN1j2nSC}%bvAMB)!PHWKHssr68q9cYguwJ@EEW83nB0W=0k`W93l?i)l9fF_cRl`bcEN0{JN!uvL&PZ#9~^NgmyIIlV9W= zr5dIwr){K*`SH5`!)q@minNGDSKSXe-qe{*5E|9XTA5dw^cSyF$*6Te{(q_AdKxbs z{hdqilFOwymwYcJB`7IhioYrdz9fF><|YDdyC+!0=5o5Fa_Kn}CzSN!>t(b(t;GFp zi^=6jyBcwR_ugHy8{-jzh#|h-b-YA)lxcflBbTF+xh5ENS_UNN0$TulKb*@5O43t` zDU=z-i*b>oOuI^sr$u+{odIuGv0A4;BnJ%h(0sNPx@o&=VDeY>{5!Vi)xb&)t*u7_PCwsP`K!=ESCSxj9*}51CuK>l=JhmGdTuky1Sy{!t6@tch0%VrxJ(f zN8BTKj>>r{L!VX7)K#D3JL{i-4$1>YKXjifeYZVM=Jmw8j9BTAg#Y0`cGejy?F2kH zqadD~A5kbfe^+a&U!0TY=VmB}q`%93eKzN{5Xxi`nfxcjUinG|*X-PC}ydyLDXlLXnCTlKJ@$CEA-0)e}87}V< zaNXF~OWcV$kfpJnRDu-b$`#(=r;jyXLT0hEUQbx;xM>NfpW!G4cK$?FgXr;yJGUJ& zK%yF@Ntqlp{Z40%fBOj0+o7 z!2}*owSUsa-q{=f>>$c3l>MzPfOz9jNBr$-LM|@P$*DQ? zZcVj5sg9TN$l+Fj`I_lEBq`)ZjD__l934(dC@!6bn<8qT>&9&V&y%3*DtoZ$@ zwN{qf7G@u5iiz)ev$L`)+`)Ina*uR{Lc}@zXb>qXiTB$iO@i7rhK$O4vfQosbc#~w z#pBriRXyg_ftwqh^i!Mpt{c_$-hB-*j0gy|rdQp8T6Ng^x|IB$+f$u;FOdENer*ow zDss=g-EMDmx23XdHy~854e1#0E;?W#X){Sd4&D`Dl07W(#WlijAJ6g^Ybvc@%v zJ3`u*F$`VvMr;XmrzoZFc0}m(A$hFq+3U?Kb(6S}(hx4Q8~eIFyY^H5Ghb1hc`u8` zhbIqUNSOMxoXZAL%^3Xcbk6$Ee8QmO#7LFEUF6n|<*;N1^215%p}hphYxC*2U1*AI zQL&FWbXJy=r*~~_P;Jz@?K_7x;kt14{Oj$;L{c1C5|6Jh7T#Va%AF_6L}ZPI{(NXJ zvg;G4lFy>9Ar9`g&d46R++aPyrH z?S;f(|B;V1GIC8#C&>Mq2-$xvbgk%-22lXSi3slB8_?b`$dDu6e)_=qi1B)aI7#l9ERy)&M+)G_^{VK5XzcwSlDN79T`JeS}4hN5| zk$y9?v7FZ8c~70)p){))=B_`>zW0{}kI05>tv;1&=bUZDeRKtt0Ze5ID3pi7&g&p@ z$LVN@v+2!7%N1rs*~H{7sORzcImAEc#Hn0+qbAQ_V$AUSX*X0goC#$2P$AQWYDT1u~FK;zJCJwKPKs zRs-H%@cn}A-B*3z0~_OXAO~~WadEb+o+U2WXcoYV2R!&MLB1F{5ZlO9FOGu0SoaHB z?2AEYCd4RWZr>4x2w~W4=_1WmntH;ARFN9j*nfTq9(q6UUIP{a$?e^~9cT~7d=_n%dKtt;he#2aphXnj{(@dX1B01Vh^G&w232e}FOJjG5kELp!kk8u z+^C&RHq15JjC`%r-s*M)w3cHXksr^t*J{752_Ah!NT{WOxpAO#Co-d2(O*!dSj#b^6Q;EJkWJQ{P;h$*P&I>x z#Ee|b0ruJVQThgk`%#6p`|4Wepe(wyd3OIzQpNS-?_mVmH?(J+F(wrmi{y`7Lh@;``j?oyh`S zcc2XeKU8_a6EG~-9JYG}bC%*TrRlt05PYBHCo^ZFn6gh9wDD8HZBA+|E-n{OnmzpB z$?sLnTpsq%%)O#4dcMcd438+v>!s1mK}- zta1Rt6zm1N=X^uCK&Rn}p$}Oo?Saz98b92bpsn3Q8qTT0cgk>8xg{{6lnO$O?c#Bm zt?%T4joX;wtp1oBm<_TXhn!mnucjEY&UhzofpHHwt^awhxy9r9VuKHuYfVVu*k zH6s&Rl%y4-CK&ko0<5&sx5y*F%|5>u)-nvC0ZN$}d}2~reyJP5p~4ZNi}|nId*GZB z@njP9+CW8oLG+NFNGE1VQBEM-IMZXyZMYyGrxP5#%HMqEeQv!$u&ic5KWW-(r!w%r zt8bNr-CNSdp!-x=;>HiWv*{Uq4aK`(Y)*WmGQgOk1CqyY)2|a zqnl!v4RGyRZTZ5S6K|HZLVJy%o4~VR|3bbE{j!Om0c(>|9eca;E-spz@GRt2&_KX~G?q1_$uGwaWO1R5VGLQ6Za3QO-D6L9+jhE4 zDP@w!(pY6P1z+?35skln@bNL7MdNr@vqBw1(1vvFps7pmzQebvjPWh4!8urH>(Wqt z@j#wq?NHfsZ$t1ez|befVTCq=M}Fv8w&lQsEdWYjE1($sI?Vf8i>KaMWKELuWOa4e zMNtVIKQMK$M{PO=JAUX+Qv8}#w+JxIIuF-~F4+nrE89tYe+A+Yc9^FsTpydex2pye z;vPE+tcdz{!2%s3x5A&RBg`-Qo_wjl0V2CLbs|gdy7MTiUHeRUtLS z9DampTbUIZDk!(RN~)SqqKBh%q4`y*fMn)`46TjHCYt{OeV{;w=1#io0{R^9DN&+1 zo=AC`@An+@=~ju!q`?d?8}!~ zsgG^BeD++_cx%BhQMXgYWbf+b~*44(n+?k1FBCJqT;#t+(p2OBs8;V^`hLcG$}guu~@C-KV{pkwf;G z4Kzj^B=1J8Z|h&z01R1LcX0bT>oVI_c+_m|SpqfmhOUz={>fRxlVO$CuYA!93V_14 zt$Z`6bi`WtD-hv8nz)N^WDjkz1XXP+2(=Wi>B0Fn@FLJLW8rENe!9P<*l@e>&A8f# zn_X|+uu-s2zMpQ-ciIQd#SZC~e?J-^HIomw!9_g5mS&`D(eCEUDR9OCPiC;-=$Bfl z$GV%A0mMg3k^X~%fQor_)7?0pj->cu8RAtt5LQE;{5&j2U&wE4u4vKksKmZL|L09j zXAF%nn{Gf2%$(!fKOpOzSH({Pikt9T$2*Oi^uNW@=f?}P2G_sKhvy{{_K4n z#O)m57vD!J#b(j$o|P^^eg+qsCWB)Y;xw&QTp;*=FY(4A@#lJlSz#`i;$fqS#L71o zW?=4o*Zz^vT-2a~wOFX5#fP>Y#xrUa;ifBK(QxO)?mXiCF-E6$K#s@XR|IfgPnBO- z9|<$R5c=f)+sl&!K(*ggqxHgMt*l|Hi@=E1Gm`_s(MQ&XDE+R2Kf{)@P5LT&BAuvG zRLkzS_9j#T+en%oy2>op<{oaHpDbWVAVzE_$wk~#iV!JS`m_Bf&?(n>DkoN5JJMKk$0((d7CAjLC(Y zat=J`wS}Ek;fv{B!wg1JYdD)dTwPUtL9AkNapus^+GJYuthsM7Fl~aki{CWz#s%so z`G>61b;6r2j37k2YjyOvS_lWbZDsZ4TCR0g-H^7{#pUiIXq`33dV$8GTUW2=o#Qam zIbefLJVT^i*Vtd8la&KEAQoD(AfJ;P-D=&)o|xjW;kJMAzUdL$Re<-m_VS*_1O1mi zqVbW4B=}o%nx&ndxb2AME|-WgfjtP}Patkddx9OFf!i7gM2mYbPa);{zvIXS zQ8Lwfyq4zX)8jnKXL|VLqu?VKsg1`fP(U(DT{UMJq}-B=of|ZJc?Dj@tlxv+p2kW_ zB$NdL&#emLk9%=uG`vBWb-S#8U!u3)a}m;4a3acXORU_!ti15^brmZ}vU%_bbiUZ? z&vn)aSKGX>wXIDll`Ljt1^xbL^^iJs)1uAiyI?j}PQ?f|S| zxZ*=*KXxJx_s2-;HMv!#Y)W!$S@An?_^*gcgjrYex-O*ZR|8RAL z^&iR6gR_6_9rykA?9;;zR=BS771p%0-vJ01S_4PRruLiZ+(@TaR_4P@aoIA@HG(t7 zwhR|P@yzf4807>wpXC2nFtjQlIEh;r2xtVG>tYa}3vC1EV0#`;gzD{|TllCShYYRi zDqk9>8a+`l(eq$xm?9MrwBrBNL?DURtVRg0aYkASt_5g?HJ?kHE;yQ(i?o(J=tT2X z7Crv{8D6K1i3Ru~$wC!>qwmhtyPDhw|3<*5V0)cE5$gi@L<*g}_rAr&XzQ_vgX$U+ zHqdW*TOph((4)CIi@(G~y{L+r>0|;OvEWbbt*i!g?t*18d40ZSCcXstj)FMk=T6-8 zywuN7d??Fy#_z5wMr0(FVtu zK=rV6nlbaGB9#xxhZR8hu4>8DRpj{LVWQHHs61|gI#RNY=i6f}t#=#>?%N?o{^{s>K4wUDvH`1~0pL)WFvs^E?k=Z;{sYRuGV^DR7=r zPINUx`iTqAd$wW;2V%p_8K58bASheBX z7#Gs|j+c@>)e&hCEY^&RggyJd%{uQvdKyzy2n1p#U2pF~oR-JSii&_<8vhyupiRg~ zsG0>n681yrcS9gWoey$ntZa){KmOVtq28!kSUEK4#N7D{-UjAG7%yXdh-3jIqRaS@ zHcRZ_O(=`N-ar2r)xtDti59W$Vu(-hWCX)ruX~}5qCT(8Z;FsTn9+8os3IuTKVQ-w z)Y4Wbl)|?sSgs6u3x?&ba4x!jmeOsCCy1eIZwGo>DdW16**ZGqWUxA*ZHb)KQZD>h zyFm4tU*MgZSBwuzM$am~?XRQ@6t`XEhr~-=Vuno%I$< zC7SNIQ^B@3Q8rbR6!u;iv;C3x73_u`Y?UU2C6|L({_|zqsirH%fwir6lu__Gdi+p; zMe39(N4TI62DMdFuX*D7?OXL=HzwnO-u6oS9vbX_NW1J$0IW`>Rm{)ZRBxuYDffkI zl)A&*BVnmy>c$(t_L6jLHvJ`xSq*>aYGDk|NmkKLK?bN-nVMM<{Kwe)W$V(%fS=Xw zZC&b|e?{>wUCwNWtyK%>=Jd9G$+_g53axaHL8Gf4FsxLqF>_nx z+)=bvgKC}RK`y(}_pBLcX-}0nJlK|( z1>8aBlIvN|a?2NAC2~f^SUF_w|4b2(C$^+C${XrCEejOD$Ynh6Wak~V@@!_?bOIgs z&)!ThL#Ze3CUa9hVM(JPs=~UOBS?6;mrUyDJiI=Ss8}p^e{b14TlUxR3RA-=5&%_d z2~(1*qtdD}6vHy`_yW`+v3?T+&ufTfc+eCGyt3IHI*-(Wr#XMj^rx*7oyy(rlNxKR zmh~L|IT3iq*jg6ScoQVcpLpsbv)l{b7s2MIM)$w!?A6bT_&5G8s|ZaCo@4}R&}#l~ z%~g%IG9%h!#8(s12yc|lB9QP!r<vY-}boQjH>qdbSSnHykp$mKRQQf^$TX?hO9$X+kR~*{>vbfsjy#+91&%&zxU}vnU zSthG~xz4y0w4K<(B)X9^zxo&^v-~Tn#SVL@HK=Os=!s3Qq6PTt;yTN3xy{!gm&otU zsC?@aBEK50JmYywO$OIWWK9QgT z**fkP`08eC(;N0&NnIC`#_dC7CiqGINwVY$+Jt7Ebhzl$lrNac zcN*yfjyD~puh3M?#op2t25kBhDH!_a8XR$9-bVuU68QsE#?=DXj^25e4+{Rel~{Lb z^XYTA?vzG=#`4R9{Vu_2Mg=6n?R-X2d9`f{=MP+F_Y>+;O6^hNU{w3o`jQ9oOH-h_ zw{a|&IzB#r4UIZXEy#*rYJH^P(I?7}24PDDyXQYCb1&HQ@)b6uEzkssWKr;t%|ifn zX079|3+Rgnb;k-frG%WLT5KXwjNU(Cs+rAx#e9f7ZVlgXt68@aH#bgZjKfE)9?2SZ z>y27iBGfvv|6g|c9xtKX8$BeKp>KG^6|D;Qp6}ngZZ%`P>B34mA*EQ{h9_`Ab2?_% zQyjbis5y~4=$@NWCDw}Ah@ETqEAw^n{@b^j)9akJT!T%ts$URj`L6S{!gGpFJ-Ir9 zqV(}(JZsZhPC{j5=5p_?y<`qPKS&!dx!9-OB4pE<4lPoxlD9kbNy+M5ruT#8;4=!K zf&OzxCXh1u$$DP#A@vd=>+I~^%E%T&uAWrQK}-Ls!R2qXw3_&pFg74HcO5>goMvB- zPb`zz3+@+tj^)yuiEf z%F1Z*&oA}F-`>M~n?b~z3d}2ic1uGHK(5bqJ!Nc7cP3^rq9-ea8%#m=ntrLFEiCNLMSCLx}KJlGUE>U23M z%^swZ@^yp9$l!jW`7vwtPC<;0de@U`^CGj+1({)bDOBF#+Mj-fcIRtMmi+hi0YSrT zY_6!=qn7@8x0ambhl}|P)4;oQtD4B+SH^;$M*`Y9-yZ1!cgw*H%?lY1pBCn17&Cxz zfD&7ncbq4Bh6Y*%ex(0x++A(9Ygq8?)ZOwUb1TO4M@|6jD}(2-AmrL1C?(XT_lfSl zb4-O^2v4KHwsOL_4NPuY2fF*r{;z?EklSkgm#d&%aTA zK|;;Wz$ITdm+;kZ6ljAw{1@J?A@fs1nV(2hA+^JJ=fi=34H}GytE{E@^LvL3~#fjD?lp)AIZ}`#U_TD3^ zs!o%>tGW@#=ps&EC+r?aPmA^7&); zq@wew+F$|<%9MZyF>T4g)+$6CRQ>GiW8%aXD8x%FhUqwZ%m4ZO@7@n^zNMtr^NSap z|7=8TbRCH+UnY~)_V%K74+l(i-uLiXsHZc|Y-PHfDc$eRyKBvmq|`rXsIg`RB1zy{xR_aCxoZ%2REF$5oHsMEoBX C;jQBU literal 0 HcmV?d00001 diff --git a/stacks/dashboard/config/www/icons/onlyoffice.png b/stacks/dashboard/config/www/icons/onlyoffice.png new file mode 100644 index 0000000000000000000000000000000000000000..685cb6c1f4a6e7c738be858d6e1d91054f608c3c GIT binary patch literal 20971 zcmdqIgWHC@lQ2_t|hP<4##%q25PeDd}{f|C25PhwH z9vZUm0rk`5hp#6Y)_U?b%E|!dR~Q+90K^4={vmn&0f3|cgnz*RfC7;0zhDg@7Xa=R zkMmj#qU-_i|Dkbwt^YaXUdt=@ziUY=7Yl1@c_(vQYYl62E5BDfs0jGqI*Q=_i~qg| z^xyC+9e}Yq>f*IRah22e007YN{wY8}P98A;0LNyprRS-qtR!UV;>2$Lj}mrYC)a&B`49W+nJA5&r>CnB2ZxW354#T!yNkOm2bZ9rAO|Nm2RAp{D+QZ} zpR=dAFPpOm?Z1WmA34(29+vL*uAcTT&eZ?NHMel_@)V_^`A5-z9sg;klk0yea`yO7 zuU`Ad;cM>7!NtzW@qc6Tw72>1=Ka68{<-pRwtw~N-^xT@O(P`bZf)-A;;!Z5;wbjN z2S|kDe^dN-OGW+>DWtDq?cw6+^-qt)__;+m{y)h7&7kUTZ~bbVe=%_VhyH)!{x`ke z|4qUFME-|CgySEB{tpBHd-VQ=eH}hA)YrcJpQ#{*YIdZ$0su$=#CFQ)~m{eZnRnJ_{g^E?@7WwXeuSmF8z*dqWSC$u_Q-WW_j4=t} z6_^QA;Y+k*Gy^;PLe!|yUV#FHd`HSTH3(oG1}>H$d<8_&gjPgKiow9ITR^E$)*>*_ zpU|_}Pg)8H@*Dz1!s$x@L6@utRu|EcaEJ$yNazU7;8#F@c6<*90|9*&1B)2d3it}N z6s1nllK#^m%Ktw#h?)bffXji3YmhlK5l8BsYH*J?F$8;eUP7o)p^<$I$ zaS{G3g?@wS$>m|*&lI$ud=_pto+>yCRsctF5NrgINZo)ZIMHW=S1U$QmB>l z96^BdVx9gTle5&i%=l3RfK3_3E8{SW%h?b8UEHD*&&=80`Jqy@0tn0g{Ft;+mE4=5+ zl?p(FwVtNOHD>i^cA}%JwxtJ>I-RvS{XUSxb1L8oj`4)DnmuV%mPk%}OX3|?df+!L zpI&^buSe*!Q3>s5H8u#$JuATJ%|G4uRu2&-emK~_7=mS=*>mL zz8IllD5R$80-&ngoO-kY2d92%0DuElfhrjHhl-LbvjA`hJbqHe?6fjiis>6kU=i~% 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 literal 0 HcmV?d00001 diff --git a/stacks/dashboard/config/www/icons/pihole.svg b/stacks/dashboard/config/www/icons/pihole.svg new file mode 100644 index 0000000..5495ad1 --- /dev/null +++ b/stacks/dashboard/config/www/icons/pihole.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/stacks/dashboard/config/www/icons/uptimekuma.svg b/stacks/dashboard/config/www/icons/uptimekuma.svg new file mode 100644 index 0000000..eaf0f3f --- /dev/null +++ b/stacks/dashboard/config/www/icons/uptimekuma.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/stacks/dashboard/config/www/icons/vaultwarden.png b/stacks/dashboard/config/www/icons/vaultwarden.png new file mode 100644 index 0000000000000000000000000000000000000000..2e4becff6f14f1887b498d9f796629c4f05c99bb GIT binary patch literal 6766 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 apuIPfLgoB83;(~Zk-mkJzmAXfH`ds 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 literal 0 HcmV?d00001 diff --git a/stacks/dashboard/config/www/index.html b/stacks/dashboard/config/www/index.html new file mode 100644 index 0000000..8351def --- /dev/null +++ b/stacks/dashboard/config/www/index.html @@ -0,0 +1,34 @@ + + + Welcome to our server + + + +
+

Welcome to our server

+

The website is currently being setup under this address.

+

For help and support, please contact: me@example.com

+
+ + diff --git a/stacks/dashboard/config/www/logs/laravel-2026-03-05.log b/stacks/dashboard/config/www/logs/laravel-2026-03-05.log new file mode 100644 index 0000000..2df875e --- /dev/null +++ b/stacks/dashboard/config/www/logs/laravel-2026-03-05.log @@ -0,0 +1,307 @@ +[2026-03-05 02:13:25] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:13:25] local.DEBUG: Process Apps dispatched +[2026-03-05 02:13:25] local.DEBUG: Process Apps dispatched +[2026-03-05 02:52:38] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:52:38] local.DEBUG: Process Apps dispatched +[2026-03-05 02:52:40] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:52:40] local.DEBUG: Process Apps dispatched +[2026-03-05 02:52:42] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:52:42] local.DEBUG: Process Apps dispatched +[2026-03-05 02:52:46] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:52:46] local.DEBUG: Process Apps dispatched +[2026-03-05 02:52:46] local.DEBUG: Get app triggered for: ba05dd8e070851895ee6184eb9778cfa0753a490 +[2026-03-05 02:52:46] local.DEBUG: Download triggered for stdClass Object +( + [appid] => ba05dd8e070851895ee6184eb9778cfa0753a490 + [name] => FileBrowser + [website] => https://github.com/filebrowser/filebrowser + [license] => Apache License 2.0 + [description] => filebrowser provides a file managing interface within a specified directory and it can be used to upload, delete, preview, rename and edit your files. It allows the creation of multiple users and each user can have its own directory. It can be used as a standalone app or as a middleware. + [enhanced] => 1 + [tile_background] => dark + [icon] => filebrowser.svg + [sha] => b92328bdd28ebaf4103fa1a7ba22da49e3c263e7 + [class] => App\SupportedApps\FileBrowser\FileBrowser +) + +[2026-03-05 02:52:47] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:52:56] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:52:57] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:53:02] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:53:03] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:53:09] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:53:09] local.DEBUG: Get app triggered for: 668b5fcda851fe516fef14e82973beffe32f385a +[2026-03-05 02:53:09] local.DEBUG: Download triggered for stdClass Object +( + [appid] => 668b5fcda851fe516fef14e82973beffe32f385a + [name] => ownCloud + [website] => https://owncloud.org + [license] => GNU Affero General Public License v3.0 or later + [description] => ownCloud is a free and open source file hosting service. It also supports extensions for online document editing, calendar and contact synchronization. It's a safe home for all your data. + [enhanced] => + [tile_background] => dark + [icon] => owncloud.png + [sha] => fa9bc697bec8367d9f4d4e98e9a34ea9c61d4710 + [class] => App\SupportedApps\ownCloud\ownCloud +) + +[2026-03-05 02:53:17] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:53:17] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:53:20] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:53:22] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:53:51] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:53:51] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:53:53] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:53:54] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:00] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:00] local.DEBUG: Get app triggered for: b89920409bdce40e08ba1023480b0546061cd577 +[2026-03-05 02:54:00] local.DEBUG: Download triggered for stdClass Object +( + [appid] => b89920409bdce40e08ba1023480b0546061cd577 + [name] => Pi-hole + [website] => https://pi-hole.net + [license] => European Union Public License 1.2 + [description] => Pi-hole is a Linux network-level advertisement and internet tracker blocking application which acts as a DNS sinkhole, intended for use on a private network. + [enhanced] => 1 + [tile_background] => dark + [icon] => pihole.svg + [sha] => 945fe7045036e17eff1d0c46be6280f207cef77a + [class] => App\SupportedApps\Pihole\Pihole +) + +[2026-03-05 02:54:00] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:11] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:11] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:13] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:14] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:18] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:18] local.DEBUG: Get app triggered for: 0bafb882c9d2c034ca5333367f8ccc90f4ae85e4 +[2026-03-05 02:54:18] local.DEBUG: Download triggered for stdClass Object +( + [appid] => 0bafb882c9d2c034ca5333367f8ccc90f4ae85e4 + [name] => Dockge + [website] => https://github.com/louislam/dockge + [license] => MIT License + [description] => A fancy, easy-to-use and reactive self-hosted docker compose.yaml stack-oriented manager. + [enhanced] => + [tile_background] => dark + [icon] => dockge.svg + [sha] => f4e77826a682041f4ab5e3276d3a7c1886d8095d + [class] => App\SupportedApps\Dockge\Dockge +) + +[2026-03-05 02:54:27] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:27] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:29] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:31] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:34] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:34] local.DEBUG: Get app triggered for: 348c49dd03dddd418929316668d2e67bf2d9ae88 +[2026-03-05 02:54:34] local.DEBUG: Download triggered for stdClass Object +( + [appid] => 348c49dd03dddd418929316668d2e67bf2d9ae88 + [name] => Bookstack + [website] => https://www.bookstackapp.com + [license] => MIT License + [description] => BookStack is a simple, self-hosted, easy-to-use platform for organising and storing information. + [enhanced] => 1 + [tile_background] => dark + [icon] => bookstack.svg + [sha] => fd57e5a47f09f6227b62b74428765dbce7f5813d + [class] => App\SupportedApps\Bookstack\Bookstack +) + +[2026-03-05 02:54:34] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:42] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:42] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:47] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:49] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:53] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:54:53] local.DEBUG: Get app triggered for: af7b37e2841d9150f6abd5a936b32a1f681d6bda +[2026-03-05 02:54:53] local.DEBUG: Download triggered for stdClass Object +( + [appid] => af7b37e2841d9150f6abd5a936b32a1f681d6bda + [name] => JDownloader + [website] => http://jdownloader.org + [license] => Creative Commons Attribution Non Commercial Share Alike 2.0 Generic + [description] => JDownloader is a free, open-source download management tool with a huge community of developers that makes downloading as easy and fast as it should be. + [enhanced] => + [tile_background] => dark + [icon] => jdownloader.png + [sha] => 32303a80935e2fbbecb1132e2358498ba876060b + [class] => App\SupportedApps\JDownloader\JDownloader +) + +[2026-03-05 02:55:00] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:55:17] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:55:17] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:55:20] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:55:22] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:55:24] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:55:24] local.DEBUG: Get app triggered for: 176d99d897dbd7c02b1a1db4142054f74a76aa47 +[2026-03-05 02:55:24] local.DEBUG: Download triggered for stdClass Object +( + [appid] => 176d99d897dbd7c02b1a1db4142054f74a76aa47 + [name] => Dozzle + [website] => https://dozzle.dev + [license] => MIT License + [description] => Dozzle is a real-time log viewer for docker containers. + [enhanced] => + [tile_background] => dark + [icon] => dozzle.svg + [sha] => 46ca9ab4e1428db7e545d09261f487b08220d28d + [class] => App\SupportedApps\Dozzle\Dozzle +) + +[2026-03-05 02:55:32] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:55:32] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:55:36] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:55:37] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:55:42] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:55:42] local.DEBUG: Get app triggered for: fc4e407d69510b855b678aa4fba6083fbbfc5383 +[2026-03-05 02:55:42] local.DEBUG: Download triggered for stdClass Object +( + [appid] => fc4e407d69510b855b678aa4fba6083fbbfc5383 + [name] => OnlyOffice + [website] => https://www.onlyoffice.com + [license] => GNU Affero General Public License v3.0 or later + [description] => ONLYOFFICE online editors for text documents, spreadsheets, and presentations with access to pro features and connect them to the platform of your choice with ready-to-use connectors: https://www.onlyoffice.com/download.aspx#connectors + [enhanced] => + [tile_background] => light + [icon] => onlyoffice.png + [sha] => 4b8aa18f29760fab9ce5cd94976e86e464772a85 + [class] => App\SupportedApps\OnlyOffice\OnlyOffice +) + +[2026-03-05 02:55:42] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:55:48] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:55:48] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:55:51] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:55:52] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:55:55] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:55:55] local.DEBUG: Get app triggered for: cbfad988a16a9fbcc1812bc206afcc1f73dd36de +[2026-03-05 02:55:55] local.DEBUG: Download triggered for stdClass Object +( + [appid] => cbfad988a16a9fbcc1812bc206afcc1f73dd36de + [name] => Nginx Proxy Manager + [website] => https://nginxproxymanager.jc21.com + [license] => MIT License + [description] => This project comes as a pre-built docker image that enables you to easily forward to your websites running at home or otherwise, including free SSL, without having to know too much about Nginx or Letsencrypt. + [enhanced] => 1 + [tile_background] => light + [icon] => nginxproxymanager.png + [sha] => 0b8e03a7f9a833c0e644f401ee85926b8ba86c73 + [class] => App\SupportedApps\NginxProxyManager\NginxProxyManager +) + +[2026-03-05 02:55:55] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:55:55] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:56:02] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:56:02] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:56:06] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:56:07] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:56:10] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:56:10] local.DEBUG: Get app triggered for: 366c6646eedab83cc4b349f198424d2291cbfa76 +[2026-03-05 02:56:10] local.DEBUG: Download triggered for stdClass Object +( + [appid] => 366c6646eedab83cc4b349f198424d2291cbfa76 + [name] => Uptime Kuma + [website] => https://uptime.kuma.pet + [license] => MIT License + [description] => It is a self-hosted monitoring tool like "Uptime Robot". + [enhanced] => 1 + [tile_background] => dark + [icon] => uptimekuma.svg + [sha] => 5179586b1259c3eba3fbf0c4712436110adb1885 + [class] => App\SupportedApps\UptimeKuma\UptimeKuma +) + +[2026-03-05 02:56:10] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:56:18] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:56:19] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:56:29] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:56:33] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:56:41] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:56:41] local.DEBUG: Get app triggered for: b5f16344632fdfe68391a2dc3816ee0edbb1813c +[2026-03-05 02:56:41] local.DEBUG: Download triggered for stdClass Object +( + [appid] => b5f16344632fdfe68391a2dc3816ee0edbb1813c + [name] => IT-Tools + [website] => https://it-tools.tech/ + [license] => GNU General Public License v3.0 only + [description] => Useful tools for developer and people working in IT. + [enhanced] => + [tile_background] => light + [icon] => ittools.png + [sha] => c42fe8dccaae8c320230a00f24771745edc8dc2d + [class] => App\SupportedApps\ITTools\ITTools +) + +[2026-03-05 02:56:41] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:56:47] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:56:48] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:56:50] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:56:51] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:56:56] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:56:56] local.DEBUG: Get app triggered for: ddf4f264320af1347ef54d424c60fae3b4fcc448 +[2026-03-05 02:56:56] local.DEBUG: Download triggered for stdClass Object +( + [appid] => ddf4f264320af1347ef54d424c60fae3b4fcc448 + [name] => Vaultwarden + [website] => https://github.com/dani-garcia/vaultwarden + [license] => GNU Affero General Public License v3.0 + [description] => Alternative implementation of the Bitwarden server API written in Rust and compatible with upstream Bitwarden clients*, perfect for self-hosted deployment where running the official resource-heavy service might not be ideal. + [enhanced] => + [tile_background] => light + [icon] => vaultwarden.png + [sha] => fbc0ce92000160c6576cf4dc54b3859098f9faa8 + [class] => App\SupportedApps\Vaultwarden\Vaultwarden +) + +[2026-03-05 02:56:57] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:57:03] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:57:03] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:57:05] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:57:06] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:57:10] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:57:10] local.DEBUG: Get app triggered for: 3e0a7f109bd760b9474c78cb652e8c3e82669226 +[2026-03-05 02:57:10] local.DEBUG: Download triggered for stdClass Object +( + [appid] => 3e0a7f109bd760b9474c78cb652e8c3e82669226 + [name] => Jellyfin + [website] => https://jellyfin.github.io + [license] => GNU General Public License v2.0 only + [description] => Jellyfin is the Free Software Media System that puts you in control of managing and streaming your media. There are no strings attached, no premium licenses or features, and no hidden agendas. + [enhanced] => 1 + [tile_background] => dark + [icon] => jellyfin.svg + [sha] => 0261afdcd2000d2ed0fddf978d364b8ffcae961d + [class] => App\SupportedApps\Jellyfin\Jellyfin +) + +[2026-03-05 02:57:11] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:57:18] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:57:19] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:57:20] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:57:21] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:57:25] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:57:25] local.DEBUG: Get app triggered for: afef2217e82ee20638490bb102605f6e09789093 +[2026-03-05 02:57:25] local.DEBUG: Download triggered for stdClass Object +( + [appid] => afef2217e82ee20638490bb102605f6e09789093 + [name] => WireGuard + [website] => https://www.wireguard.com + [license] => CNRI Python Open Source GPL Compatible License Agreement + [description] => WireGuard® is an extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography. It aims to be faster, simpler, leaner, and more useful than IPsec, while avoiding the massive headache. It intends to be considerably more performant than OpenVPN. WireGuard is designed as a general purpose VPN for running on embedded interfaces and super computers alike, fit for many different circumstances. Initially released for the Linux kernel, it is now cross-platform (Windows, macOS, BSD, iOS, Android) and widely deployable. It is currently under heavy development, but already it might be regarded as the most secure, easiest to use, and simplest VPN solution in the industry. + [enhanced] => + [tile_background] => dark + [icon] => wireguard.png + [sha] => 5c05f64749725c3179f109d21977d086101b3ee7 + [class] => App\SupportedApps\WireGuard\WireGuard +) + +[2026-03-05 02:57:31] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:57:32] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:57:34] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:57:35] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:57:49] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite +[2026-03-05 02:57:49] local.DEBUG: SQLite Database Path: /app/www/database/app.sqlite diff --git a/stacks/dashboard/config/www/searchproviders.yaml b/stacks/dashboard/config/www/searchproviders.yaml new file mode 100644 index 0000000..bbb13d6 --- /dev/null +++ b/stacks/dashboard/config/www/searchproviders.yaml @@ -0,0 +1,44 @@ +tiles: + id: tiles + name: Tiles + target: _blank + +baidu: + id: baidu + url: https://www.baidu.com/s + name: Baidu + method: get + target: _blank + query: wd + +bing: + id: bing + url: https://www.bing.com/search + name: Bing + method: get + target: _blank + query: q + +ddg: + id: ddg + url: https://duckduckgo.com/ + name: DuckDuckGo + method: get + target: _blank + query: q + +google: + id: google + url: https://www.google.com/search + name: Google + method: get + target: _blank + query: q + +startpage: + id: startpage + url: https://www.startpage.com/do/dsearch + name: Startpage + method: get + target: _blank + query: query