including some more dashboard components

This commit is contained in:
2026-03-05 06:34:27 +01:00
parent f03ec900c6
commit 7d8c36a771
57 changed files with 1694 additions and 0 deletions

View File

@@ -0,0 +1,83 @@
<?php
namespace App\SupportedApps\Bookstack;
class Bookstack extends \App\SupportedApps implements \App\EnhancedApps
{
public $config;
//protected $login_first = true; // Uncomment if api requests need to be authed first
//protected $method = 'POST'; // Uncomment if requests to the API should be set by POST
public function __construct()
{
//$this->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",
];
}
}

View File

@@ -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"
}

View File

@@ -0,0 +1,22 @@
<h2>{{ __('app.apps.config') }} ({{ __('app.optional') }}) @include('items.enable')</h2>
<div class="items">
<div class="input">
<label>{{ strtoupper(__('app.url')) }}</label>
{!! Form::text('config[override_url]', isset($item) ? $item->getConfig()->override_url ?? null : null, ['placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control']) !!}
</div>
<div class="input">
<label>Token ID</label>
{!! Form::text('config[api_token]', isset($item) ? $item->getconfig()->api_token : null, ['placeholder' => 'Token ID', 'data-config' => 'api_token', 'class' => 'form-control config-item']) !!}
</div>
<div class="input">
<label>Token Secret</label>
{!! Form::text('config[api_secret]', isset($item) ? $item->getconfig()->api_secret : null, ['placeholder' => 'Token Secret', 'data-config' => 'api_secret', 'class' => 'form-control config-item']) !!}
</div>
<div class="input">
<label>Stats to show</label>
{!! Form::select('config[availablestats][]', App\SupportedApps\Bookstack\Bookstack::getAvailableStats(), isset($item) ? $item->getConfig()->availablestats ?? null : null, ['multiple' => 'multiple']) !!}
</div>
<div class="input">
<button style="margin-top: 32px;" class="btn test" id="test_config">Test</button>
</div>
</div>

View File

@@ -0,0 +1,8 @@
<ul class="livestats">
@foreach ($visiblestats as $stat)
<li>
<span class="title">{!! $stat->title !!}</span>
<strong>{!! $stat->value !!}</strong>
</li>
@endforeach
</ul>

View File

@@ -0,0 +1,7 @@
<?php
namespace App\SupportedApps\Dockge;
class Dockge extends \App\SupportedApps
{
}

View File

@@ -0,0 +1,10 @@
{
"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": false,
"tile_background": "dark",
"icon": "dockge.svg"
}

View File

@@ -0,0 +1,7 @@
<?php
namespace App\SupportedApps\Dozzle;
class Dozzle extends \App\SupportedApps
{
}

View File

@@ -0,0 +1,10 @@
{
"appid": "176d99d897dbd7c02b1a1db4142054f74a76aa47",
"name": "Dozzle",
"website": "https://dozzle.dev",
"license": "MIT License",
"description": "Dozzle is a real-time log viewer for docker containers.",
"enhanced": false,
"tile_background": "dark",
"icon": "dozzle.svg"
}

View File

@@ -0,0 +1,101 @@
<?php
namespace App\SupportedApps\FileBrowser;
class FileBrowser extends \App\SupportedApps implements \App\EnhancedApps
{
public $config;
//protected $login_first = true; // Uncomment if api requests need to be authed first
//protected $method = 'POST'; // Uncomment if requests to the API should be set by POST
public function __construct()
{
//$this->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;
}
}

View File

@@ -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"
}

View File

@@ -0,0 +1,29 @@
<h2>{{ __('app.apps.config') }} ({{ __('app.optional') }}) @include('items.enable')</h2>
<div class="items" style="flex-direction:column">
<div style="display:flex;flex-direction:row;">
<div class="input">
<label>{{ strtoupper(__('app.url')) }}</label>
{!! Form::text('config[override_url]', isset($item) ? $item->getconfig()->override_url : null, ['placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control']) !!}
</div>
</div>
<div style="display:flex;flex-direction:row;">
<div class="input">
<label>{{ __('app.apps.username') }}</label>
{!! Form::text('config[username]', isset($item) ? $item->getconfig()->username : null, ['placeholder' => __('app.apps.username'), 'data-config' => 'username', 'class' => 'form-control config-item']) !!}
</div>
<div class="input">
<label>{{ __('app.apps.password') }}</label>
{!! Form::input('password', 'config[password]', '', ['placeholder' => __('app.apps.password'), 'data-config' => 'password', 'class' => 'form-control config-item']) !!}
</div>
</div>
<div style="display:flex;flex-direction:row;">
<div class="input">
<label>Proxy Header (<a href="https://filebrowser.org/configuration/authentication-method#proxy-header" target="_blank">help?</a>)</label>
{!! Form::text('config[authHeader]', isset($item) ? $item->getconfig()->authHeader : null, ['placeholder' => 'X-My-Header', 'data-config' => 'authHeader', 'class' => 'form-control config-item']) !!}
<small>Also fill {{ __('app.apps.username') }}</small>
</div>
</div>
<div class="input">
<button style="margin-top: 32px;" class="btn test" id="test_config">Test</button>
</div>
</div>

View File

@@ -0,0 +1,10 @@
<ul class="livestats">
<li>
<span class="title">Used</span>
<strong>{!! $used["value"] !!}<span>{!! $used["unit"] !!}</span></strong>
</li>
<li>
<span class="title">Total</span>
<strong>{!! $total["value"] !!}<span>{!! $total["unit"] !!}</span></strong>
</li>
</ul>

View File

@@ -0,0 +1,7 @@
<?php
namespace App\SupportedApps\ITTools;
class ITTools extends \App\SupportedApps
{
}

View File

@@ -0,0 +1,10 @@
{
"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": false,
"tile_background": "light",
"icon": "ittools.png"
}

View File

@@ -0,0 +1,7 @@
<?php
namespace App\SupportedApps\JDownloader;
class JDownloader extends \App\SupportedApps
{
}

View File

@@ -0,0 +1,10 @@
{
"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": false,
"tile_background": "dark",
"icon": "jdownloader.png"
}

View File

@@ -0,0 +1,71 @@
<?php
namespace App\SupportedApps\Jellyfin;
class Jellyfin extends \App\SupportedApps implements \App\EnhancedApps
{
public $config;
public function __construct()
{
}
public function test()
{
$test = parent::appTest(
$this->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",
];
}
}

View File

@@ -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"
}

View File

@@ -0,0 +1,18 @@
<h2>{{ __('app.apps.config') }} ({{ __('app.optional') }}) @include('items.enable')</h2>
<div class="items">
<div class="input">
<label>{{ strtoupper(__('app.url')) }}</label>
{!! Form::text('config[override_url]', isset($item) ? $item->getconfig()->override_url : null, ['placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control']) !!}
</div>
<div class="input">
<label>{{ __('app.apps.password') }} (secret token)</label>
{!! Form::input('password', 'config[password]', '', ['placeholder' => __('app.apps.password'), 'data-config' => 'password', 'class' => 'form-control config-item']) !!}
</div>
<div class="input">
<label>Stats to show</label>
{!! Form::select('config[availablestats][]', App\SupportedApps\Jellyfin\Jellyfin::getAvailableStats(), isset($item) ? $item->getConfig()->availablestats ?? null : null, ['multiple' => 'multiple']) !!}
</div>
<div class="input">
<button style="margin-top: 32px;" class="btn test" id="test_config">Test</button>
</div>
</div>

View File

@@ -0,0 +1,8 @@
<ul class="livestats">
@foreach ($visiblestats as $stat)
<li>
<span class="title">{!! $stat->title !!}</span>
<strong>{!! $stat->value !!}</strong>
</li>
@endforeach
</ul>

View File

@@ -0,0 +1,61 @@
<?php
namespace App\SupportedApps\NginxProxyManager;
class NginxProxyManager extends \App\SupportedApps implements \App\EnhancedApps
{
public $config;
//protected $login_first = true; // Uncomment if api requests need to be authed first
//protected $method = 'POST'; // Uncomment if requests to the API should be set by POST
public function __construct()
{
//$this->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;
}
}

View File

@@ -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"
}

View File

@@ -0,0 +1,18 @@
<h2>{{ __('app.apps.config') }} ({{ __('app.optional') }}) @include('items.enable')</h2>
<div class="items">
<div class="input">
<label>{{ strtoupper(__('app.url')) }}</label>
{!! Form::text('config[override_url]', isset($item) ? $item->getconfig()->override_url : null, ['placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control']) !!}
</div>
<div class="input">
<label>Email address</label>
{!! Form::text('config[email]', isset($item) ? $item->getconfig()->email : null, ['placeholder' => __('Email address'), 'data-config' => 'email', 'class' => 'form-control config-item']) !!}
</div>
<div class="input">
<label>{{ __('app.apps.password') }}</label>
{!! Form::input('password', 'config[password]', '', ['placeholder' => __('app.apps.password'), 'data-config' => 'password', 'class' => 'form-control config-item']) !!}
</div>
<div class="input">
<button style="margin-top: 32px;" class="btn test" id="test_config">Test</button>
</div>
</div>

View File

@@ -0,0 +1,10 @@
<ul class="livestats">
<li>
<span class="title">Proxy Hosts</span>
<strong>{!! $proxy !!}</strong>
</li>
<li>
<span class="title">Redirection Hosts</span>
<strong>{!! $redirection !!}</strong>
</li>
</ul>

View File

@@ -0,0 +1,7 @@
<?php
namespace App\SupportedApps\OnlyOffice;
class OnlyOffice extends \App\SupportedApps
{
}

View File

@@ -0,0 +1,10 @@
{
"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": false,
"tile_background": "light",
"icon": "onlyoffice.png"
}

View File

@@ -0,0 +1,184 @@
<?php
namespace App\SupportedApps\Pihole;
use Illuminate\Support\Facades\Log;
class Pihole extends \App\SupportedApps implements \App\EnhancedApps
{
public $config;
//protected $login_first = true; // Uncomment if api requests need to be authed first
//protected $method = 'POST'; // Uncomment if requests to the API should be set by POST
public function __construct()
{
$this->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;
}
}

View File

@@ -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"
}

View File

@@ -0,0 +1,43 @@
<h2>{{ __('app.apps.config') }} ({{ __('app.optional') }}) @include('items.enable')</h2>
<div class="items">
<input type="hidden" data-config="dataonly" class="config-item" name="config[dataonly]" value="1" />
<div class="input">
<label>{{ strtoupper(__('app.url')) }}</label>
{!! Form::text('config[override_url]', isset($item) ? $item->getconfig()->override_url : null, ['placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control']) !!}
</div>
<div class="input">
<label>Api Key(v5)/App Password(v6)</label>
{!! 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']) !!}
</div>
<div class="items" style="max-width: 100px;">
<div class="input">
<label>Version</label>
{!! Form::select(
'config[version]',
['5' => 'v5', '6' => 'v6'],
isset($item) ? $item->getconfig()->version : null,
['data-config' => 'version', 'class' => 'form-control config-item'],
) !!}
</div>
</div>
<div class="input" style="max-width: 150px;">
<label>Skip TLS verification</label>
<div class="toggleinput" style="margin-top: 26px; padding-left: 15px;">
{!! Form::hidden('config[ignore_tls]', 0, ['class' => 'config-item', 'data-config' => 'ignore_tls']) !!}
<label class="switch">
<?php
$checked = false;
if (isset($item) && !empty($item) && isset($item->getconfig()->ignore_tls)) {
$checked = $item->getconfig()->ignore_tls;
}
$set_checked = $checked ? ' checked="checked"' : '';
?>
<input type="checkbox" class="config-item" data-config="ignore_tls" name="config[ignore_tls]" value="1" <?php echo $set_checked; ?> />
<span class="slider round"></span>
</label>
</div>
</div>
<div class="input">
<button style="margin-top: 32px;" class="btn test" id="test_config">Test</button>
</div>
</div>

View File

@@ -0,0 +1,10 @@
<ul class="livestats">
<li>
<span class="title">Queries<br />Blocked</span>
<strong>{!! $ads_blocked !!}</strong>
</li>
<li>
<span class="title">Percent<br /> Blocked</span>
<strong>{!! $ads_percentage !!}</strong>
</li>
</ul>

View File

@@ -0,0 +1,95 @@
<?php
namespace App\SupportedApps\UptimeKuma;
class UptimeKuma extends \App\SupportedApps implements \App\EnhancedApps
{
public $config;
private $mapping = [
0 => "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;
}
}

View File

@@ -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"
}

View File

@@ -0,0 +1,32 @@
<h2>{{ __('app.apps.config') }} ({{ __('app.optional') }}) @include('items.enable')</h2>
<div class="items">
<input type="hidden" data-config="dataonly" class="config-item" name="config[dataonly]" value="1" />
<div class="input">
<label>{{ strtoupper(__('app.url')) }}</label>
{!! Form::text('config[override_url]', isset($item) ? $item->getconfig()->override_url : null, ['placeholder' => __('app.apps.override'), 'id' => 'override_url', 'class' => 'form-control']) !!}
</div>
<div class="input">
<label>{{ __('app.apps.apikey') }}</label>
{!! Form::text('config[apikey]', isset($item) ? ($item->getconfig()->apikey ?? null) : null, ['placeholder' => __('app.apps.apikey'), 'data-config' => 'apikey', 'class' => 'form-control config-item']) !!}
</div>
<div class="input">
<label>Skip TLS verification</label>
<div class="toggleinput" style="margin-top: 26px; padding-left: 15px;">
{!! Form::hidden('config[ignore_tls]', 0, ['class' => 'config-item', 'data-config' => 'ignore_tls']) !!}
<label class="switch">
<?php
$checked = false;
if (isset($item) && !empty($item) && isset($item->getconfig()->ignore_tls)) {
$checked = $item->getconfig()->ignore_tls;
}
$set_checked = $checked ? ' checked="checked"' : '';
?>
<input type="checkbox" class="config-item" data-config="ignore_tls" name="config[ignore_tls]" value="1" <?php echo $set_checked; ?> />
<span class="slider round"></span>
</label>
</div>
</div>
<div class="input">
<button style="margin-top: 32px;" class="btn test" id="test_config">Test</button>
</div>
</div>

View File

@@ -0,0 +1,10 @@
<ul class="livestats">
<li>
<span class="title">Up</span>
<strong>{!! $up ?? 0 !!}</strong>
</li>
<li>
<span class="title">Down</span>
<strong>{!! $down ?? 0 !!}</strong>
</li>
</ul>

View File

@@ -0,0 +1,7 @@
<?php
namespace App\SupportedApps\Vaultwarden;
class Vaultwarden extends \App\SupportedApps
{
}

View File

@@ -0,0 +1,10 @@
{
"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": false,
"tile_background": "light",
"icon": "vaultwarden.png"
}

View File

@@ -0,0 +1,7 @@
<?php
namespace App\SupportedApps\WireGuard;
class WireGuard extends \App\SupportedApps
{
}

View File

@@ -0,0 +1,10 @@
{
"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": false,
"tile_background": "dark",
"icon": "wireguard.png"
}

View File

@@ -0,0 +1,10 @@
{
"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": false,
"tile_background": "dark",
"icon": "owncloud.png"
}

View File

@@ -0,0 +1,7 @@
<?php
namespace App\SupportedApps\ownCloud;
class ownCloud extends \App\SupportedApps // phpcs:ignore
{
}