blowl 3 лет назад
Родитель
Сommit
6db703ab41

+ 8 - 0
api/classes/organizr.class.php

@@ -48,6 +48,7 @@ class Organizr
 	use OmbiHomepageItem;
 	use OverseerrHomepageItem;
 	use PiHoleHomepageItem;
+	use AdGuardHomepageItem;
 	use PlexHomepageItem;
 	use QBitTorrentHomepageItem;
 	use RadarrHomepageItem;
@@ -4591,6 +4592,13 @@ class Organizr
 						$class .= ' faded';
 					}
 					break;
+				case 'homepageOrderAdGuard':
+					$class = 'bg-info';
+					$image = 'plugins/images/tabs/AdGuardHomepageItem';
+					if (!$this->config['homepageAdGuardEnabled']) {
+						$class .= ' faded';
+					}
+					break;
 				case 'homepageOrderMonitorr':
 					$class = 'bg-info';
 					$image = 'plugins/images/tabs/monitorr.png';

+ 33 - 21
api/config/default.php

@@ -343,27 +343,28 @@ return [
 	'homepageOrderjdownloader' => '18',
 	'homepageOrderunifi' => '19',
 	'homepageOrderPihole' => '20',
-	'homepageOrdertautulli' => '21',
-	'homepageOrderMonitorr' => '22',
-	'homepageOrderWeatherAndAir' => '23',
-	'homepageOrderSpeedtest' => '24',
-	'homepageOrderNetdata' => '25',
-	'homepageOrderSonarrQueue' => '26',
-	'homepageOrderRadarrQueue' => '27',
-	'homepageOrderOctoprint' => '28',
-	'homepageOrderjellyfinnowplaying' => '29',
-	'homepageOrderjellyfinrecent' => '30',
-	'homepageOrderJackett' => '31',
-	'homepageOrdercustomhtml03' => '32',
-	'homepageOrdercustomhtml04' => '33',
-	'homepageOrdercustomhtml05' => '34',
-	'homepageOrdercustomhtml06' => '35',
-	'homepageOrdercustomhtml07' => '36',
-	'homepageOrdercustomhtml08' => '37',
-	'homepageOrderuTorrent' => '38',
-	'homepageOrderoverseerr' => '39',
-	'homepageOrderBookmarks' => '40',
-	'homepageOrderDonate' => '41',
+	'homepageOrderAdguard' => '21',
+	'homepageOrdertautulli' => '22',
+	'homepageOrderMonitorr' => '23',
+	'homepageOrderWeatherAndAir' => '24',
+	'homepageOrderSpeedtest' => '25',
+	'homepageOrderNetdata' => '26',
+	'homepageOrderSonarrQueue' => '27',
+	'homepageOrderRadarrQueue' => '28',
+	'homepageOrderOctoprint' => '29',
+	'homepageOrderjellyfinnowplaying' => '30',
+	'homepageOrderjellyfinrecent' => '31',
+	'homepageOrderJackett' => '32',
+	'homepageOrdercustomhtml03' => '33',
+	'homepageOrdercustomhtml04' => '34',
+	'homepageOrdercustomhtml05' => '35',
+	'homepageOrdercustomhtml06' => '36',
+	'homepageOrdercustomhtml07' => '37',
+	'homepageOrdercustomhtml08' => '38',
+	'homepageOrderuTorrent' => '39',
+	'homepageOrderoverseerr' => '40',
+	'homepageOrderBookmarks' => '41',
+	'homepageOrderDonate' => '42',
 	'homepageShowStreamNames' => false,
 	'homepageShowStreamNamesAuth' => '1',
 	'homepageShowStreamNamesWithoutIp' => false,
@@ -523,6 +524,17 @@ return [
 	'homepagePiholeCombine' => false,
 	'piholeHeaderToggle' => true,
 	'piholeURL' => '',
+	'homepageAdGuardEnabled' => false,
+	'homepageAdGuardAuth' => '1',
+	'homepageAdGuardRefresh' => '10000',
+	'homepageAdGuardCombine' => false,
+	'adguardQueriesToggle' => true,
+	'adguardQueriesBlockedToggle' => true,
+	'adguardPercentToggle' => true,
+	'adguardProcessingToggle' => true,
+	'adguardDomainListToggle' => false,
+	'adguardHeaderToggle' => true,
+	'adguardURL' => '',
 	'homepageMonitorrEnabled' => false,
 	'homepageMonitorrAuth' => '1',
 	'homepageMonitorrRefresh' => '60000',

+ 30 - 0
api/functions/option-functions.php

@@ -263,6 +263,36 @@ trait OptionsFunction
 					'help' => 'Shows/hides the title of this homepage module'
 				];
 				break;
+			case 'total-queries':
+				$settingsMerge = [
+					'type' => 'switch',
+					'label' => 'Total Queries',
+					'help' => 'Shows/hides the total queries stat'
+				];
+			case 'queries-blocked':
+				$settingsMerge = [
+					'type' => 'switch',
+					'label' => 'Total Queries',
+					'help' => 'Shows/hides the queries blocked stat'
+				];
+			case 'percent-blocked':
+				$settingsMerge = [
+					'type' => 'switch',
+					'label' => 'Total Queries',
+					'help' => 'Shows/hides the percent blocked stat'
+				];
+			case 'processing-time':
+				$settingsMerge = [
+					'type' => 'switch',
+					'label' => 'Total Queries',
+					'help' => 'Shows/hides the processing time stat'
+				];
+			case 'domain-list':
+				$settingsMerge = [
+					'type' => 'switch',
+					'label' => 'Total Queries',
+					'help' => 'Shows/hides the domain list stat'
+				];
 			case 'disablecertcheck':
 				$settingMerge = [
 					'type' => 'switch',

+ 176 - 0
api/homepage/adguard.php

@@ -0,0 +1,176 @@
+<?php
+
+trait AdGuardHomepageItem
+{
+	public function adguardSettingsArray($infoOnly = false)
+	{
+		$homepageInformation = [
+			'name' => 'AdGuardHome',
+			'enabled' => true,
+			'image' => 'plugins/images/tabs/AdGuardHome.png',
+			'category' => 'Monitor',
+			'settingsArray' => __FUNCTION__
+		];
+		if ($infoOnly) {
+			return $homepageInformation;
+		}
+		$homepageSettings = [
+			'debug' => true,
+			'settings' => [
+				'Enable' => [
+					$this->settingsOption('enable', 'homepageAdGuardEnabled'),
+					$this->settingsOption('auth', 'homepageAdGuardAuth'),
+				],
+				'Connection' => [
+					$this->settingsOption('url', 'adguardURL', ['help' => 'Please make sure to use local IP address and port. You can add multiple AdGuard Homes by comma separating the URLs.', 'placeholder' => 'http(s)://hostname:port']),
+          $this->settingsOption('username', 'adGuardUsername'),
+					$this->settingsOption('password', 'adGuardPassword'),
+        ],
+				'Misc' => [
+					$this->settingsOption('toggle-title', 'adguardToggle'),
+					$this->settingsOption('switch', 'homepageAdGuardCombine', ['label' => 'Combine stat cards', 'help' => 'This controls whether to combine the stats for multiple adguard instances into 1 card.']),
+				],
+				'Stats' => [
+					$this->settingsOption('switch', 'adguardQueriesToggle', ['label' => 'Total Queries']),
+					$this->settingsOption('switch', 'adguardQueriesBlockedToggle', ['label' => 'Queries Blocked']),
+					$this->settingsOption('switch', 'adguardPercentToggle', ['label' => 'Percent Blocked']),
+					$this->settingsOption('switch', 'adguardProcessingToggle', ['label' => 'Processing Time']),
+					$this->settingsOption('switch', 'adguardDomainListToggle', ['label' => 'Domains on Blocklist']),
+				],
+				'Test Connection' => [
+					$this->settingsOption('blank', null, ['label' => 'Please Save before Testing']),
+					$this->settingsOption('test', 'adguard'),
+				]
+			]
+		];
+		return array_merge($homepageInformation, $homepageSettings);
+	}
+
+	public function testConnectionAdGuard()
+	{
+		if (empty($this->config['adguardURL'])) {
+			$this->setAPIResponse('error', 'AdGuard URL is not defined', 422);
+			return false;
+		}
+		$api = array();
+		$failed = false;
+		$errors = '';
+		$urls = explode(',', $this->config['adguardURL']);
+		foreach ($urls as $url) {
+			$url = $url . '/control/stats';
+			try {
+				$options = array(
+					'auth' => array($this->config['adGuardUsername'], $this->decrypt($this->config['adGuardPassword']))
+				);
+				$response = Requests::get($url, [], $options);
+				if ($response->success) {
+					@$test = json_decode($response->body, true);
+					if (!is_array($test)) {
+						$ip = $this->qualifyURL($url, true)['host'];
+						$errors .= $ip . ': Response was not JSON';
+						$failed = true;
+					}
+				}
+				if (!$response->success) {
+					$ip = $this->qualifyURL($url, true)['host'];
+					$errors .= $ip . ": Unknown Failure";
+					$failed = true;
+				}
+			} catch (Requests_Exception $e) {
+				$failed = true;
+				$ip = $this->qualifyURL($url, true)['host'];
+				$errors .= $ip . ': ' . $e->getMessage();
+				$this->setLoggerChannel('AdGuard')->error($e);
+			};
+		}
+		if ($failed) {
+			$this->setAPIResponse('error', $errors, 500);
+			return false;
+		} else {
+			$this->setAPIResponse('success', null, 200);
+			return true;
+		}
+	}
+
+	public function adguardHomepagePermissions($key = null)
+	{
+		$permissions = [
+			'main' => [
+				'enabled' => [
+					'homepageAdGuardEnabled'
+				],
+				'auth' => [
+					'homepageAdGuardAuth'
+				],
+				'not_empty' => [
+					'adguardURL'
+				]
+			]
+		];
+		return $this->homepageCheckKeyPermissions($key, $permissions);
+	}
+
+	public function homepageOrderAdGuard()
+	{
+		if ($this->homepageItemPermissions($this->adguardHomepagePermissions('main'))) {
+			return '
+				<div id="' . __FUNCTION__ . '">
+					<div class="white-box homepage-loading-box"><h2 class="text-center" lang="en">Loading AdGuard...</h2></div>
+					<script>
+						// Pi-hole Stats
+						homepageAdGuard("' . $this->config['homepageAdGuardRefresh'] . '");
+						// End Pi-hole Stats
+					</script>
+				</div>
+				';
+		}
+	}
+
+	public function getAdGuardHomepageStats()
+	{
+		if (!$this->homepageItemPermissions($this->adguardHomepagePermissions('main'), true)) {
+			return false;
+		}
+		$stats = array();
+		$urls = explode(',', $this->config['adguardURL']);
+		foreach ($urls as $url) {
+			$stats_url = $url . '/control/stats?';
+			$filter_url = $url . '/control/filtering/status?';
+			try {
+				$options = array(
+					'auth' => array($this->config['adGuardUsername'], $this->decrypt($this->config['adGuardPassword']))
+				);
+				$response = Requests::get($stats_url, [], $options);
+				if ($response->success) {
+					@$adguardResults = json_decode($response->body, true);
+					if (is_array($adguardResults)) {
+						$ip = $this->qualifyURL($stats_url, true)['host'];
+						$stats['data'][$ip] = $adguardResults;
+					}
+				}
+				$response = Requests::get($filter_url, [], $options);
+				if ($response->success) {
+					@$adguardFilterResults = json_decode($response->body, true);
+					if (is_array($adguardFilterResults)) {
+						$ip = $this->qualifyURL($filter_url, true)['host'];
+						$stats['filters'][$ip] = $adguardFilterResults;
+					}
+				}
+			} catch (Requests_Exception $e) {
+				$this->setResponse(500, $e->getMessage());
+				$this->setLoggerChannel('AdGuard')->error($e);
+				return false;
+			};
+		}
+		$stats['options']['combine'] = $this->config['homepageAdGuardCombine'];
+		$stats['options']['title'] = $this->config['adguardHeaderToggle'];
+		$stats['options']['queries'] = $this->config['adguardQueriesToggle'];
+		$stats['options']['blocked_count'] = $this->config['adguardQueriesBlockedToggle'];
+		$stats['options']['blocked_percent'] = $this->config['adguardPercentToggle'];
+		$stats['options']['processing_time'] = $this->config['adguardProcessingToggle'];
+		$stats['options']['domain_count'] = $this->config['adguardDomainListToggle'];
+		$stats = isset($stats) ? $stats : null;
+		$this->setAPIResponse('success', null, 200, $stats);
+		return $stats;
+	}
+}

+ 22 - 0
api/v2/routes/connectionTester.php

@@ -200,6 +200,28 @@ $app->post('/test/pihole', function ($request, $response, $args) {
 		->withHeader('Content-Type', 'application/json;charset=UTF-8')
 		->withStatus($GLOBALS['responseCode']);
 });
+$app->post('/test/adguard', function ($request, $response, $args) {
+	/**
+ * @OA\Post(
+ *     security={{ "api_key":{} }},
+ *     tags={"test connection"},
+ *     path="/api/v2/test/adguard",
+ *     summary="Test connection to AdGuard",
+ *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+ *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+ *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+ *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+ * )
+ */
+$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+if ($Organizr->qualifyRequest(1, true)) {
+	$Organizr->testConnectionAdGuard($Organizr->apiData($request));
+}
+$response->getBody()->write(jsonE($GLOBALS['api']));
+return $response
+	->withHeader('Content-Type', 'application/json;charset=UTF-8')
+	->withStatus($GLOBALS['responseCode']);
+});
 $app->post('/test/rtorrent', function ($request, $response, $args) {
 	/**
 	 * @OA\Post(

+ 8 - 0
api/v2/routes/homepage.php

@@ -112,6 +112,14 @@ $app->get('/homepage/pihole/stats', function ($request, $response, $args) {
 		->withHeader('Content-Type', 'application/json;charset=UTF-8')
 		->withStatus($GLOBALS['responseCode']);
 });
+$app->get('/homepage/adguard/stats', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getAdGuardHomepageStats();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
 $app->get('/homepage/rtorrent/queue', function ($request, $response, $args) {
 	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
 	$Organizr->getRTorrentHomepageQueue();

+ 281 - 0
js/functions.js

@@ -7272,6 +7272,29 @@ function buildPihole(array){
     `;
     return (array) ? html : '';
 }
+function buildAdGuard(array){
+    if(array === false){ return ''; }
+    var html = `
+    <div id="allPihole">
+        <div class="el-element-overlay row">`;
+    if(array['options']['title']) {
+        html += `
+            <div class="col-md-12">
+                <h4 class="pull-left homepage-element-title"><span lang="en">AdGuard Home</span> : </h4><h4 class="pull-left">&nbsp;</h4>
+                <hr class="hidden-xs ml-2">
+            </div>
+            <div class="clearfix"></div>
+        `;
+    }
+    html += `
+		    <div class="piholeCards col-sm-12 my-3">
+			    `+buildAdGuardItem(array)+`
+			</div>
+		</div>
+	</div>
+    `;
+    return (array) ? html : '';
+}
 function buildUnifi(array){
     if(array === false){ return ''; }
     var items = (typeof array.content.unifi.data !== 'undefined') ? array.content.unifi.data.length : false;
@@ -7555,6 +7578,243 @@ function arrayRemove(arr, value) {
 		return ele != value;
 	});
 }
+function buildAdGuardItem(array){
+    var stats = `
+    <style>
+    .bg-green {
+        background-color: #00a65a !important;
+    }
+    
+    .bg-aqua {
+        background-color: #00c0ef!important;
+    }
+    
+    .bg-yellow {
+        background-color: #f39c12!important;
+    }
+    
+    .bg-red {
+        background-color: #dd4b39!important;
+    }
+    
+    .pihole-stat {
+        color: #fff !important;
+    }
+    
+    .pihole-stat .card-body h3 {
+        font-size: 38px;
+        font-weight: 700;
+    }
+
+    .pihole-stat .card-body i {
+        font-size: 5em;
+        float: right;
+        color: #ffffff6b;
+    }
+
+    .inline-block {
+        display: inline-block;
+    }
+    </style>
+    `;
+    var length = Object.keys(array['data']).length;
+    var combine = array['options']['combine'];
+    var totalQueries = function(data) {
+        var card = `
+        <div class="col-lg-3 col-md-6 col-sm-6 col-xs-12">
+            <div class="card text-white mb-3 pihole-stat bg-green">
+                <div class="card-body">
+                    <div class="inline-block">
+                        <p class="d-inline mr-1">Total Queries</p>`;
+        for(var key in data) {
+            var e = data[key];
+	        if(length > 1 && !combine) {
+		        card += `<p class="d-inline text-muted">(`+key+`)</p>`;
+	        }
+	        card += `<h3 data-toggle="tooltip" data-placement="right" title="`+key+`">`+e['num_dns_queries'].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")+`</h3>`;
+        };
+        card += `
+                    </div>
+                    <i class="fa fa-globe inline-block" aria-hidden="true"></i>
+                </div>
+            </div>
+        </div>
+        `
+        return card;
+    };
+    var totalBlocked = function(data) {
+        var card = `
+        <div class="col-lg-3 col-md-6 col-sm-6 col-xs-12">
+            <div class="card bg-inverse text-white mb-3 pihole-stat bg-aqua">
+                <div class="card-body">
+                    <div class="inline-block">
+                        <p class="d-inline mr-1">Queries Blocked</p>`;
+        for(var key in data) {
+            var e = data[key];
+		    if (length > 1 && !combine) {
+			    card += `<p class="d-inline text-muted">(${key})</p>`;
+		    }
+		    card += `<h3 data-toggle="tooltip" data-placement="right" title="${key}">${e['num_blocked_filtering'].toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}</h3>`;
+        };
+        card += `
+                    </div>
+                    <i class="fa fa-hand-paper-o inline-block" aria-hidden="true"></i>
+                </div>
+            </div>
+        </div>
+        `
+        return card;
+    };
+    var avgProcessingTime = function(data) {
+        var card = `
+        <div class="col-lg-3 col-md-6 col-sm-6 col-xs-12">
+            <div class="card bg-inverse text-white mb-3 pihole-stat bg-purple">
+                <div class="card-body">
+                    <div class="inline-block">
+                        <p class="d-inline mr-1">Avg Processing Time</p>`;
+        for(var key in data) {
+            var e = data[key];
+		        if (length > 1 && !combine) {
+			        card += `<p class="d-inline text-muted">(${key})</p>`;
+		        }
+                ms_time = parseFloat(e['avg_processing_time'])*1000
+		        card += `<h3 data-toggle="tooltip" data-placement="right" title="${key}">${ms_time.toFixed(2)} ms</h3>`;
+        };
+        card += `
+                    </div>
+                    <i class="fa fa-group inline-block" aria-hidden="true"></i>
+                </div>
+            </div>
+        </div>
+        `
+        return card;
+    };
+    var domainsBlocked = function(data) {
+        var card = `
+        <div class="col-lg-3 col-md-6 col-sm-6 col-xs-12">
+            <div class="card bg-inverse text-white mb-3 pihole-stat bg-red">
+                <div class="card-body">
+                    <div class="inline-block">
+                        <p class="d-inline mr-1">Domains on Blocklist</p>`;
+        for(var key in data) {
+            var e = data[key];
+		        if (length > 1 && !combine) {
+			        card += `<p class="d-inline text-muted">(${key})</p>`;
+		        }
+                var total_domains_blocked = 0
+                for(var key in e['filters']){
+                    total_domains_blocked += parseFloat(e['filters'][key]['rules_count'])
+                }
+		        card += `<h3 data-toggle="tooltip" data-placement="right" title="${key}">${total_domains_blocked.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}</h3>`;
+        };
+        card += `
+                    </div>
+                    <i class="fa fa-list inline-block" aria-hidden="true"></i>
+                </div>
+            </div>
+        </div>
+        `
+        return card;
+    };
+    var domainsBlocked = function(data) {
+        var card = `
+        <div class="col-lg-3 col-md-6 col-sm-6 col-xs-12">
+            <div class="card bg-inverse text-white mb-3 pihole-stat bg-red">
+                <div class="card-body">
+                    <div class="inline-block">
+                        <p class="d-inline mr-1">Domains on Blocklist</p>`;
+        for(var key in data) {
+            var e = data[key];
+		        if (length > 1 && !combine) {
+			        card += `<p class="d-inline text-muted">(${key})</p>`;
+		        }
+                var total_domains_blocked = 0
+                for(var key in e['filters']){
+                    total_domains_blocked += parseFloat(e['filters'][key]['rules_count'])
+                }
+                total_domains_blocked += Object.keys(e['user_rules']).length
+		        card += `<h3 data-toggle="tooltip" data-placement="right" title="${key}">${total_domains_blocked.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}</h3>`;
+        };
+        card += `
+                    </div>
+                    <i class="fa fa-list inline-block" aria-hidden="true"></i>
+                </div>
+            </div>
+        </div>
+        `
+        return card;
+    };
+    var percentBlocked = function(data) {
+        var card = `
+        <div class="col-lg-3 col-md-6 col-sm-6 col-xs-12">
+            <div class="card bg-inverse text-white mb-3 pihole-stat bg-yellow">
+                <div class="card-body">
+                    <div class="inline-block">
+                        <p class="d-inline mr-1">Percent Blocked</p>`;
+        for(var key in data) {
+            var e = data[key];
+	        if(typeof e['FTLnotrunning'] == 'undefined') {
+		        if (length > 1 && !combine) {
+			        card += `<p class="d-inline text-muted">(${key})</p>`;
+		        }
+                var percent = 100*(parseFloat(e['num_blocked_filtering'])/parseFloat(e['num_dns_queries']))
+		        card += `<h3 data-toggle="tooltip" data-placement="right" title="${key}">${percent.toFixed(2)}%</h3>`
+	        }
+        };
+        card += `
+                    </div>
+                    <i class="fa fa-pie-chart inline-block" aria-hidden="true"></i>
+                </div>
+            </div>
+        </div>
+        `
+        return card;
+    };
+        if(combine) {
+            stats += '<div class="row">'
+            if(array['options']['queries']){
+                stats += totalQueries(array['data']);
+            }
+            if(array['options']['blocked_count']){
+                stats += totalBlocked(array['data']);
+            }
+            if(array['options']['blocked_percent']){
+                stats += percentBlocked(array['data']);
+            }
+            if(array['options']['processing_time']){
+                stats += avgProcessingTime(array['data']);
+            }
+            if(array['options']['domain_count']){
+                stats += domainsBlocked(array['filters']);
+            }
+            stats += '</div>';
+        } else {
+            for(var key in array['data']) {
+                var data = array['data'][key];
+                obj = {};
+                obj[key] = data;
+                stats += '<div class="row">'
+                if(array['options']['queries']){
+                    stats += totalQueries(array['data']);
+                }
+                if(array['options']['blocked_count']){
+                    stats += totalBlocked(array['data']);
+                }
+                if(array['options']['blocked_percent']){
+                    stats += percentBlocked(array['data']);
+                }
+                if(array['options']['processing_time']){
+                    stats += avgProcessingTime(array['data']);
+                }
+                if(array['options']['domain_count']){
+                    stats += domainsBlocked(array['filters']);
+                }
+                stats += '</div>';
+            };
+        };
+        return stats
+}
+
 function buildPiholeItem(array){
     var stats = `
     <style>
@@ -7740,6 +8000,27 @@ function homepagePihole(timeout){
     timeouts[timeoutTitle] = setTimeout(function(){ homepagePihole(timeout); }, timeout);
     delete timeout;
 }
+function homepageAdGuard(timeout){
+    var timeout = (typeof timeout !== 'undefined') ? timeout : activeInfo.settings.homepage.refresh.homepagePiholeRefresh;
+    organizrAPI2('GET','api/v2/homepage/adguard/stats').success(function(data) {
+        try {
+            let response = data.response;
+	        document.getElementById('homepageOrderAdGuard').innerHTML = '';
+	        if(response.data !== null){
+		        buildAdGuard(response.data)
+		        $('#homepageOrderAdGuard').html(buildAdGuard(response.data));
+	        }
+        }catch(e) {
+	        organizrCatchError(e,data);
+        }
+    }).fail(function(xhr) {
+	    OrganizrApiError(xhr);
+    });
+    let timeoutTitle = 'AdGuard-Homepage';
+    if(typeof timeouts[timeoutTitle] !== 'undefined'){ clearTimeout(timeouts[timeoutTitle]); }
+    timeouts[timeoutTitle] = setTimeout(function(){ homepageAdGuard(timeout); }, timeout);
+    delete timeout;
+}
 function homepageHealthChecks(tags, timeout){
     tags = (typeof tags !== 'undefined') ? tags : activeInfo.settings.homepage.options.healthChecksTags;
     if(tags == ''){