mirror of
https://github.com/pi-hole/web.git
synced 2026-05-08 09:39:05 +01:00
Merge branch 'devel' into patch-1
This commit is contained in:
+22
-4
@@ -71,7 +71,11 @@ if (isset($_GET['overTimeData10mins']))
|
||||
|
||||
if (isset($_GET['topItems']) && $auth)
|
||||
{
|
||||
if(is_numeric($_GET['topItems']))
|
||||
if($_GET['topItems'] === "audit")
|
||||
{
|
||||
sendRequestFTL("top-domains for audit");
|
||||
}
|
||||
else if(is_numeric($_GET['topItems']))
|
||||
{
|
||||
sendRequestFTL("top-domains (".$_GET['topItems'].")");
|
||||
}
|
||||
@@ -88,7 +92,11 @@ if (isset($_GET['topItems']) && $auth)
|
||||
$top_queries[$tmp[2]] = intval($tmp[1]);
|
||||
}
|
||||
|
||||
if(is_numeric($_GET['topItems']))
|
||||
if($_GET['topItems'] === "audit")
|
||||
{
|
||||
sendRequestFTL("top-ads for audit");
|
||||
}
|
||||
else if(is_numeric($_GET['topItems']))
|
||||
{
|
||||
sendRequestFTL("top-ads (".$_GET['topItems'].")");
|
||||
}
|
||||
@@ -102,7 +110,10 @@ if (isset($_GET['topItems']) && $auth)
|
||||
foreach($return as $line)
|
||||
{
|
||||
$tmp = explode(" ",$line);
|
||||
$top_ads[$tmp[2]] = intval($tmp[1]);
|
||||
if(count($tmp) === 4)
|
||||
$top_ads[$tmp[2]." (".$tmp[3].")"] = intval($tmp[1]);
|
||||
else
|
||||
$top_ads[$tmp[2]] = intval($tmp[1]);
|
||||
}
|
||||
|
||||
$result = array('top_queries' => $top_queries,
|
||||
@@ -153,7 +164,14 @@ if ((isset($_GET['topClients']) || isset($_GET['getQuerySources'])) && $auth)
|
||||
|
||||
if (isset($_GET['getForwardDestinations']) && $auth)
|
||||
{
|
||||
sendRequestFTL("forward-dest");
|
||||
if($_GET['getForwardDestinations'] === "unsorted")
|
||||
{
|
||||
sendRequestFTL("forward-dest unsorted");
|
||||
}
|
||||
else
|
||||
{
|
||||
sendRequestFTL("forward-dest");
|
||||
}
|
||||
$return = getResponseFTL();
|
||||
$forward_dest = array();
|
||||
foreach($return as $line)
|
||||
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license */
|
||||
|
||||
$api = true;
|
||||
header('Content-type: application/json');
|
||||
require("scripts/pi-hole/php/password.php");
|
||||
require("scripts/pi-hole/php/auth.php");
|
||||
check_cors();
|
||||
|
||||
$data = array();
|
||||
|
||||
// Needs package php5-sqlite, e.g.
|
||||
// sudo apt-get install php5-sqlite
|
||||
|
||||
$db = new SQLite3('/etc/pihole/pihole-FTL.db');
|
||||
if(!$db)
|
||||
die("Cannot access database");
|
||||
|
||||
// Long-Term API functions
|
||||
// if (isset($_GET['test']))
|
||||
// {
|
||||
// // $results = $db->query('SELECT * FROM QUERIES order by TIMESTAMP ASC LIMIT 2');
|
||||
// $results = $db->query('SELECT TIMESTAMP,TYPE,DOMAIN,CLIENT,STATUS FROM QUERIES order by TIMESTAMP ASC');
|
||||
// echo "N=".$result->numColumns;
|
||||
// while ($row = $results->fetchArray())
|
||||
// var_dump($row);
|
||||
// }
|
||||
|
||||
if (isset($_GET['getAllQueries']) && $auth)
|
||||
{
|
||||
if($_GET['getAllQueries'] === "empty")
|
||||
{
|
||||
$allQueries = array();
|
||||
}
|
||||
else
|
||||
{
|
||||
$from = intval($_GET["from"]);
|
||||
$until = intval($_GET["until"]);
|
||||
$results = $db->query('SELECT timestamp,type,domain,client,status FROM queries WHERE timestamp >= '.$from.' AND timestamp <= '.$until.' ORDER BY timestamp ASC');
|
||||
$allQueries = array();
|
||||
while ($row = $results->fetchArray())
|
||||
{
|
||||
$allQueries[] = [$row[0],$row[1] == 1 ? "IPv4" : "IPv6",$row[2],$row[3],$row[4]];
|
||||
}
|
||||
}
|
||||
$result = array('data' => $allQueries);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if (isset($_GET['topClients']) && $auth)
|
||||
{
|
||||
// $from = intval($_GET["from"]);
|
||||
$limit = "";
|
||||
if(isset($_GET["from"]) && isset($_GET["until"]))
|
||||
{
|
||||
$limit = "WHERE timestamp >= ".$_GET["from"]." AND timestamp <= ".$_GET["until"];
|
||||
}
|
||||
elseif(isset($_GET["from"]) && !isset($_GET["until"]))
|
||||
{
|
||||
$limit = "WHERE timestamp >= ".$_GET["from"];
|
||||
}
|
||||
elseif(!isset($_GET["from"]) && isset($_GET["until"]))
|
||||
{
|
||||
$limit = "WHERE timestamp <= ".$_GET["until"];
|
||||
}
|
||||
$results = $db->query('SELECT client,count(client) FROM queries '.$limit.' GROUP by client order by count(client) desc limit 10');
|
||||
$clients = array();
|
||||
while ($row = $results->fetchArray())
|
||||
{
|
||||
$clients[$row[0]] = intval($row[1]);
|
||||
// var_dump($row);
|
||||
}
|
||||
$result = array('top_sources' => $clients);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if (isset($_GET['topDomains']) && $auth)
|
||||
{
|
||||
$limit = "";
|
||||
|
||||
if(isset($_GET["from"]) && isset($_GET["until"]))
|
||||
{
|
||||
$limit = " AND timestamp >= ".$_GET["from"]." AND timestamp <= ".$_GET["until"];
|
||||
}
|
||||
elseif(isset($_GET["from"]) && !isset($_GET["until"]))
|
||||
{
|
||||
$limit = " AND timestamp >= ".$_GET["from"];
|
||||
}
|
||||
elseif(!isset($_GET["from"]) && isset($_GET["until"]))
|
||||
{
|
||||
$limit = " AND timestamp <= ".$_GET["until"];
|
||||
}
|
||||
$results = $db->query('SELECT domain,count(domain) FROM queries WHERE (STATUS == 2 OR STATUS == 3)'.$limit.' GROUP by domain order by count(domain) desc limit 10');
|
||||
$domains = array();
|
||||
while ($row = $results->fetchArray())
|
||||
{
|
||||
$domains[$row[0]] = intval($row[1]);
|
||||
}
|
||||
$result = array('top_domains' => $domains);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if (isset($_GET['topAds']) && $auth)
|
||||
{
|
||||
$limit = "";
|
||||
|
||||
if(isset($_GET["from"]) && isset($_GET["until"]))
|
||||
{
|
||||
$limit = " AND timestamp >= ".$_GET["from"]." AND timestamp <= ".$_GET["until"];
|
||||
}
|
||||
elseif(isset($_GET["from"]) && !isset($_GET["until"]))
|
||||
{
|
||||
$limit = " AND timestamp >= ".$_GET["from"];
|
||||
}
|
||||
elseif(!isset($_GET["from"]) && isset($_GET["until"]))
|
||||
{
|
||||
$limit = " AND timestamp <= ".$_GET["until"];
|
||||
}
|
||||
$results = $db->query('SELECT domain,count(domain) FROM queries WHERE (STATUS == 1 OR STATUS == 4)'.$limit.' GROUP by domain order by count(domain) desc limit 10');
|
||||
$addomains = array();
|
||||
while ($row = $results->fetchArray())
|
||||
{
|
||||
$addomains[$row[0]] = intval($row[1]);
|
||||
}
|
||||
$result = array('top_ads' => $addomains);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if (isset($_GET['getMinTimestamp']) && $auth)
|
||||
{
|
||||
$results = $db->query('SELECT MIN(timestamp) FROM queries');
|
||||
$result = array('mintimestamp' => $results->fetchArray()[0]);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if (isset($_GET['getMaxTimestamp']) && $auth)
|
||||
{
|
||||
$results = $db->query('SELECT MAX(timestamp) FROM queries');
|
||||
$result = array('maxtimestamp' => $results->fetchArray()[0]);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if (isset($_GET['getQueriesCount']) && $auth)
|
||||
{
|
||||
$results = $db->query('SELECT COUNT(timestamp) FROM queries');
|
||||
$result = array('count' => $results->fetchArray()[0]);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if (isset($_GET['getDBfilesize']) && $auth)
|
||||
{
|
||||
$filesize = filesize("/etc/pihole/pihole-FTL.db");
|
||||
$result = array('filesize' => $filesize);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if (isset($_GET['getGraphData']) && $auth)
|
||||
{
|
||||
$limit = "";
|
||||
|
||||
if(isset($_GET["from"]) && isset($_GET["until"]))
|
||||
{
|
||||
$limit = " AND timestamp >= ".intval($_GET["from"])." AND timestamp <= ".intval($_GET["until"]);
|
||||
}
|
||||
elseif(isset($_GET["from"]) && !isset($_GET["until"]))
|
||||
{
|
||||
$limit = " AND timestamp >= ".intval($_GET["from"]);
|
||||
}
|
||||
elseif(!isset($_GET["from"]) && isset($_GET["until"]))
|
||||
{
|
||||
$limit = " AND timestamp <= ".intval($_GET["until"]);
|
||||
}
|
||||
|
||||
$interval = 600;
|
||||
|
||||
if(isset($_GET["interval"]))
|
||||
{
|
||||
$q = intval($_GET["interval"]);
|
||||
if($q > 10)
|
||||
$interval = $q;
|
||||
}
|
||||
|
||||
// Count permitted queries in intervals
|
||||
$results = $db->query('SELECT (timestamp/'.$interval.')*'.$interval.' interval, COUNT(*) FROM queries WHERE (status == 2 OR status == 3)'.$limit.' GROUP by interval ORDER by interval');
|
||||
$domains = array();
|
||||
while ($row = $results->fetchArray())
|
||||
{
|
||||
$domains[$row[0]] = intval($row[1]);
|
||||
}
|
||||
$result = array('domains_over_time' => $domains);
|
||||
$data = array_merge($data, $result);
|
||||
|
||||
// Count blocked queries in intervals
|
||||
$results = $db->query('SELECT (timestamp/'.$interval.')*'.$interval.' interval, COUNT(*) FROM queries WHERE (status == 1 OR status == 4 OR status == 5)'.$limit.' GROUP by interval ORDER by interval');
|
||||
$addomains = array();
|
||||
while ($row = $results->fetchArray())
|
||||
{
|
||||
$addomains[$row[0]] = intval($row[1]);
|
||||
}
|
||||
$result = array('ads_over_time' => $addomains);
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if(isset($_GET["jsonForceObject"]))
|
||||
{
|
||||
echo json_encode($data, JSON_FORCE_OBJECT);
|
||||
}
|
||||
else
|
||||
{
|
||||
echo json_encode($data);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php /*
|
||||
* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
require "scripts/pi-hole/php/header.php";
|
||||
?>
|
||||
<!-- Send PHP info to JS -->
|
||||
<div id="token" hidden><?php echo $token ?></div>
|
||||
<!-- Title -->
|
||||
<div class="page-header">
|
||||
<h1>Audit log (showing live data)</h1>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="box" id="domain-frequency">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Allowed queries</h3>
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<div class="box-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Domain</th>
|
||||
<th>Hits</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overlay">
|
||||
<i class="fa fa-refresh fa-spin"></i>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
<div class="col-md-6">
|
||||
<div class="box" id="ad-frequency">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Blocked queries</h3>
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<div class="box-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Domain</th>
|
||||
<th>Hits</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overlay">
|
||||
<i class="fa fa-refresh fa-spin"></i>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<p><strong>Important:</strong> Note that black- and whitelisted domains are not automatically applied on this page to avoid restarting the DNS service too often. Instead, click on this button, to have the new settings become effective:</p>
|
||||
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
<div class="container">
|
||||
<div class="row justify-content-md-right">
|
||||
<div class="col-2">
|
||||
<button class="btn btn-lg btn-primary btn-block" id="gravityBtn" disabled="true">Update black-/whitelists</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
?>
|
||||
|
||||
<script src="scripts/pi-hole/js/auditlog.js"></script>
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php /*
|
||||
* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
require "scripts/pi-hole/php/header.php";
|
||||
|
||||
// Generate CSRF token
|
||||
if(empty($_SESSION['token'])) {
|
||||
$_SESSION['token'] = base64_encode(openssl_random_pseudo_bytes(32));
|
||||
}
|
||||
$token = $_SESSION['token'];
|
||||
|
||||
?>
|
||||
<!-- Send PHP info to JS -->
|
||||
<div id="token" hidden><?php echo $token ?></div>
|
||||
|
||||
<!-- Title -->
|
||||
<div class="page-header">
|
||||
<h1>Compute graphical statistics from the Pi-hole query database</h1>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<!-- Date Input -->
|
||||
<div class="form-group">
|
||||
<label>Date and time range:</label>
|
||||
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon">
|
||||
<i class="fa fa-clock-o"></i>
|
||||
</div>
|
||||
<input type="text" class="form-control pull-right" id="querytime">
|
||||
</div>
|
||||
<!-- /.input group -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box" id="queries-over-time">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Queries over the selected time period</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="chart">
|
||||
<canvas id="queryOverTimeChart" width="800" height="250"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overlay" hidden="true">
|
||||
<i class="fa fa-refresh fa-spin"></i>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
?>
|
||||
|
||||
<script src="scripts/vendor/moment.min.js"></script>
|
||||
<script src="scripts/vendor/daterangepicker.js"></script>
|
||||
<script src="scripts/pi-hole/js/db_graph.js"></script>
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
<?php /*
|
||||
* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
require "scripts/pi-hole/php/header.php";
|
||||
|
||||
// Generate CSRF token
|
||||
if(empty($_SESSION['token'])) {
|
||||
$_SESSION['token'] = base64_encode(openssl_random_pseudo_bytes(32));
|
||||
}
|
||||
$token = $_SESSION['token'];
|
||||
|
||||
?>
|
||||
<!-- Send PHP info to JS -->
|
||||
<div id="token" hidden><?php echo $token ?></div>
|
||||
|
||||
<!-- Title -->
|
||||
<div class="page-header">
|
||||
<h1>Compute Top Lists from the Pi-hole query database</h1>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<!-- Date Input -->
|
||||
<div class="form-group">
|
||||
<label>Date and time range:</label>
|
||||
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon">
|
||||
<i class="fa fa-clock-o"></i>
|
||||
</div>
|
||||
<input type="text" class="form-control pull-right" id="querytime">
|
||||
</div>
|
||||
<!-- /.input group -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
if($boxedlayout)
|
||||
{
|
||||
$tablelayout = "col-md-6";
|
||||
}
|
||||
else
|
||||
{
|
||||
$tablelayout = "col-md-6 col-lg-4";
|
||||
}
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="<?php echo $tablelayout; ?>">
|
||||
<div class="box" id="domain-frequency">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Top Domains</h3>
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<div class="box-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Domain</th>
|
||||
<th>Hits</th>
|
||||
<th>Frequency</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overlay" hidden>
|
||||
<i class="fa fa-refresh fa-spin"></i>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
<div class="<?php echo $tablelayout; ?>">
|
||||
<div class="box" id="ad-frequency">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Top Blocked Domains</h3>
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<div class="box-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Domain</th>
|
||||
<th>Hits</th>
|
||||
<th>Frequency</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overlay" hidden>
|
||||
<i class="fa fa-refresh fa-spin"></i>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
<div class="<?php echo $tablelayout; ?>">
|
||||
<div class="box" id="client-frequency">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Top Clients</h3>
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<div class="box-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Client</th>
|
||||
<th>Requests</th>
|
||||
<th>Frequency</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overlay" hidden>
|
||||
<i class="fa fa-refresh fa-spin"></i>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
?>
|
||||
|
||||
<script src="scripts/vendor/moment.min.js"></script>
|
||||
<script src="scripts/vendor/daterangepicker.js"></script>
|
||||
<script src="scripts/pi-hole/js/db_lists.js"></script>
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
<?php /*
|
||||
* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
require "scripts/pi-hole/php/header.php";
|
||||
|
||||
// Generate CSRF token
|
||||
if(empty($_SESSION['token'])) {
|
||||
$_SESSION['token'] = base64_encode(openssl_random_pseudo_bytes(32));
|
||||
}
|
||||
$token = $_SESSION['token'];
|
||||
|
||||
?>
|
||||
<!-- Send PHP info to JS -->
|
||||
<div id="token" hidden><?php echo $token ?></div>
|
||||
|
||||
<!-- Title -->
|
||||
<div class="page-header">
|
||||
<h1>Specify date range to be queried from the Pi-hole query database</h1>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<!-- Date Input -->
|
||||
<div class="form-group">
|
||||
<label>Date and time range:</label>
|
||||
|
||||
<div class="input-group">
|
||||
<div class="input-group-addon">
|
||||
<i class="fa fa-clock-o"></i>
|
||||
</div>
|
||||
<input type="text" class="form-control pull-right" id="querytime">
|
||||
</div>
|
||||
<!-- /.input group -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Small boxes (Stat box) -->
|
||||
<div class="row">
|
||||
<div class="col-lg-3 col-xs-12">
|
||||
<!-- small box -->
|
||||
<div class="small-box bg-aqua">
|
||||
<div class="inner">
|
||||
<h3 class="statistic" id="ads_blocked_exact">---</h3>
|
||||
<p>Queries Blocked</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="ion ion-android-hand"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ./col -->
|
||||
<div class="col-lg-3 col-xs-12">
|
||||
<!-- small box -->
|
||||
<div class="small-box bg-aqua">
|
||||
<div class="inner">
|
||||
<h3 class="statistic" id="ads_wildcard_blocked">---</h3>
|
||||
<p>Queries Blocked (Wildcards)</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="ion ion-android-hand"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ./col -->
|
||||
<div class="col-lg-3 col-xs-12">
|
||||
<!-- small box -->
|
||||
<div class="small-box bg-green">
|
||||
<div class="inner">
|
||||
<h3 class="statistic" id="dns_queries">---</h3>
|
||||
<p>Queries Total</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="ion ion-earth"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ./col -->
|
||||
<div class="col-lg-3 col-xs-12">
|
||||
<!-- small box -->
|
||||
<div class="small-box bg-yellow">
|
||||
<div class="inner">
|
||||
<h3 class="statistic" id="ads_percentage_today">---</h3>
|
||||
<p>Queries Blocked</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="ion ion-pie-graph"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ./col -->
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box" id="recent-queries">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Recent Queries <?php echo $showing; ?></h3>
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<div class="box-body">
|
||||
<div class="table-responsive">
|
||||
<table id="all-queries" class="display table table-striped table-bordered" cellspacing="0" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Time</th>
|
||||
<th>Type</th>
|
||||
<th>Domain</th>
|
||||
<th>Client</th>
|
||||
<th>Status</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th>Time</th>
|
||||
<th>Type</th>
|
||||
<th>Domain</th>
|
||||
<th>Client</th>
|
||||
<th>Status</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
?>
|
||||
|
||||
<script src="scripts/vendor/moment.min.js"></script>
|
||||
<script src="scripts/vendor/daterangepicker.js"></script>
|
||||
<script src="scripts/pi-hole/js/db_queries.js"></script>
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
<?php /*
|
||||
<?php /*
|
||||
* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
@@ -9,7 +9,7 @@
|
||||
?>
|
||||
<!-- Title -->
|
||||
<div class="page-header">
|
||||
<h1>Update list of ad-serving domains</h1>
|
||||
<h1>Update list of ad-serving domains / blacklist / whitelist</h1>
|
||||
</div>
|
||||
|
||||
<!-- Alerts -->
|
||||
|
||||
@@ -10,25 +10,12 @@
|
||||
?>
|
||||
<!-- Small boxes (Stat box) -->
|
||||
<div class="row">
|
||||
<div class="col-lg-3 col-xs-12">
|
||||
<!-- small box -->
|
||||
<div class="small-box bg-aqua">
|
||||
<div class="inner">
|
||||
<h3 class="statistic" id="ads_blocked_today">---</h3>
|
||||
<p>Queries Blocked Last 24 Hours</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="ion ion-android-hand"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ./col -->
|
||||
<div class="col-lg-3 col-xs-12">
|
||||
<!-- small box -->
|
||||
<div class="small-box bg-green">
|
||||
<div class="inner">
|
||||
<h3 class="statistic" id="dns_queries_today">---</h3>
|
||||
<p>Queries Last 24 Hours</p>
|
||||
<p>Total queries (<span id="unique_clients">-</span> clients)</p>
|
||||
<h3 class="statistic"><span id="dns_queries_today">---</span></h3>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="ion ion-earth"></i>
|
||||
@@ -36,12 +23,25 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- ./col -->
|
||||
<div class="col-lg-3 col-xs-12">
|
||||
<!-- small box -->
|
||||
<div class="small-box bg-aqua">
|
||||
<div class="inner">
|
||||
<p>Queries Blocked</p>
|
||||
<h3 class="statistic"><span id="ads_blocked_today">---</span></h3>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="ion ion-android-hand"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ./col -->
|
||||
<div class="col-lg-3 col-xs-12">
|
||||
<!-- small box -->
|
||||
<div class="small-box bg-yellow">
|
||||
<div class="inner">
|
||||
<h3 class="statistic" id="ads_percentage_today">---</h3>
|
||||
<p>Queries Blocked Last 24 Hours</p>
|
||||
<p>Percent Blocked</p>
|
||||
<h3 class="statistic"><span id="ads_percentage_today">---</span></h3>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="ion ion-pie-graph"></i>
|
||||
@@ -53,8 +53,8 @@
|
||||
<!-- small box -->
|
||||
<div class="small-box bg-red">
|
||||
<div class="inner">
|
||||
<h3 class="statistic" id="domains_being_blocked">---</h3>
|
||||
<p>Domains on Blocklists</p>
|
||||
<p>Domains on Blocklist</p>
|
||||
<h3 class="statistic"><span id="domains_being_blocked">---</span></h3>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="ion ion-ios-list"></i>
|
||||
@@ -88,11 +88,46 @@
|
||||
// show since the API will respect the privacy of the user if he defines
|
||||
// a password
|
||||
if($auth){ ?>
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-lg-6">
|
||||
<div class="box" id="query-types-pie">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Query Types (integrated)</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="chart">
|
||||
<canvas id="queryTypePieChart" width="400" height="150"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overlay">
|
||||
<i class="fa fa-refresh fa-spin"></i>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12 col-lg-6">
|
||||
<div class="box" id="forward-destinations-pie">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Forward Destinations (integrated)</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="chart">
|
||||
<canvas id="forwardDestinationPieChart" width="400" height="150"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overlay">
|
||||
<i class="fa fa-refresh fa-spin"></i>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-lg-6">
|
||||
<div class="box" id="query-types">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Query Types over Time</h3>
|
||||
<h3 class="box-title">Query Types (over time)</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="chart">
|
||||
@@ -108,7 +143,7 @@
|
||||
<div class="col-md-12 col-lg-6">
|
||||
<div class="box" id="forward-destinations">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Forward Destinations over Time</h3>
|
||||
<h3 class="box-title">Forward Destinations (over time)</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="chart">
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
// Define global variables
|
||||
var timeLineChart, queryTypeChart, forwardDestinationChart;
|
||||
|
||||
// Credit: http://stackoverflow.com/questions/1787322/htmlspecialchars-equivalent-in-javascript/4835406#4835406
|
||||
function escapeHtml(text) {
|
||||
var map = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
"\"": """,
|
||||
"\'": "'"
|
||||
};
|
||||
|
||||
return text.replace(/[&<>"']/g, function(m) { return map[m]; });
|
||||
}
|
||||
|
||||
function updateTopLists() {
|
||||
$.getJSON("api.php?topItems=audit", function(data) {
|
||||
|
||||
if("FTLnotrunning" in data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear tables before filling them with data
|
||||
$("#domain-frequency td").parent().remove();
|
||||
$("#ad-frequency td").parent().remove();
|
||||
var domaintable = $("#domain-frequency").find("tbody:last");
|
||||
var adtable = $("#ad-frequency").find("tbody:last");
|
||||
var url, domain, percentage;
|
||||
for (domain in data.top_queries) {
|
||||
if ({}.hasOwnProperty.call(data.top_queries,domain)){
|
||||
// Sanitize domain
|
||||
domain = escapeHtml(domain);
|
||||
url = "<a href=\"queries.php?domain="+domain+"\">"+domain+"</a>";
|
||||
percentage = data.top_queries[domain] / data.dns_queries_today * 100;
|
||||
domaintable.append("<tr> <td>" + url +
|
||||
"</td> <td>" + data.top_queries[domain] + "</td> <td> <button style=\"color:red; white-space: nowrap;\"><i class=\"fa fa-ban\"></i> Blacklist</button> <button style=\"color:orange; white-space: nowrap;\"><i class=\"fa fa-balance-scale\"></i> Audit</button> </td> </tr> ");
|
||||
}
|
||||
}
|
||||
|
||||
for (domain in data.top_ads) {
|
||||
if ({}.hasOwnProperty.call(data.top_ads,domain)){
|
||||
var input = domain.split(" ");
|
||||
// Sanitize domain
|
||||
var printdomain = escapeHtml(input[0]);
|
||||
if(input.length > 1)
|
||||
{
|
||||
url = "<a href=\"queries.php?domain="+printdomain+"\">"+printdomain+"</a> (wildcard blocked)";
|
||||
adtable.append("<tr> <td>" + url +
|
||||
"</td> <td>" + data.top_ads[domain] + "</td> <td> <button style=\"color:orange; white-space: nowrap;\"><i class=\"fa fa-balance-scale\"></i> Audit</button> </td> </tr> ");
|
||||
}
|
||||
else
|
||||
{
|
||||
url = "<a href=\"queries.php?domain="+printdomain+"\">"+printdomain+"</a>";
|
||||
adtable.append("<tr> <td>" + url +
|
||||
"</td> <td>" + data.top_ads[domain] + "</td> <td> <button style=\"color:green; white-space: nowrap;\"><i class=\"fa fa-pencil-square-o\"></i> Whitelist</button> <button style=\"color:orange; white-space: nowrap;\"><i class=\"fa fa-balance-scale\"></i> Audit</button> </td> </tr> ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$("#domain-frequency .overlay").hide();
|
||||
$("#ad-frequency .overlay").hide();
|
||||
// Update top lists data every second
|
||||
setTimeout(updateTopLists, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function add(domain,list) {
|
||||
var token = $("#token").html();
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/add.php",
|
||||
method: "post",
|
||||
data: {"domain":domain, "list":list, "token":token, "auditlog":1}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
// Pull in data via AJAX
|
||||
updateTopLists();
|
||||
|
||||
$("#domain-frequency tbody").on( "click", "button", function () {
|
||||
var url = ($(this).parents("tr"))[0].innerText.split(" ")[0];
|
||||
if($(this).context.innerText === " Blacklist")
|
||||
{
|
||||
add(url,"black");
|
||||
$("#gravityBtn").prop("disabled", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
add(url,"audit");
|
||||
}
|
||||
});
|
||||
|
||||
$("#ad-frequency tbody").on( "click", "button", function () {
|
||||
var url = ($(this).parents("tr"))[0].innerText.split(" ")[0].split(" ")[0];
|
||||
if($(this).context.innerText === " Whitelist")
|
||||
{
|
||||
add(url,"white");
|
||||
$("#gravityBtn").prop("disabled", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
add(url,"audit");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$("#gravityBtn").on("click", function() {
|
||||
window.location.replace("gravity.php?go");
|
||||
});
|
||||
@@ -0,0 +1,262 @@
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/*global
|
||||
moment
|
||||
*/
|
||||
|
||||
var start__ = moment().subtract(6, "days");
|
||||
var from = moment(start__).utc().valueOf()/1000;
|
||||
var end__ = moment();
|
||||
var until = moment(end__).utc().valueOf()/1000;
|
||||
|
||||
$(function () {
|
||||
$("#querytime").daterangepicker(
|
||||
{
|
||||
timePicker: true, timePickerIncrement: 15,
|
||||
locale: { format: "MMMM Do YYYY, HH:mm" },
|
||||
ranges: {
|
||||
"Today": [moment().startOf("day"), moment()],
|
||||
"Yesterday": [moment().subtract(1, "days").startOf("day"), moment().subtract(1, "days").endOf("day")],
|
||||
"Last 7 Days": [moment().subtract(6, "days"), moment()],
|
||||
"Last 30 Days": [moment().subtract(29, "days"), moment()],
|
||||
"This Month": [moment().startOf("month"), moment()],
|
||||
"Last Month": [moment().subtract(1, "month").startOf("month"), moment().subtract(1, "month").endOf("month")],
|
||||
"This Year": [moment().startOf("year"), moment()],
|
||||
"All Time": [moment(0), moment()]
|
||||
},
|
||||
"opens": "center", "showDropdowns": true
|
||||
},
|
||||
function (startt, endt) {
|
||||
from = moment(startt).utc().valueOf()/1000;
|
||||
until = moment(endt).utc().valueOf()/1000;
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
// Credit: http://stackoverflow.com/questions/1787322/htmlspecialchars-equivalent-in-javascript/4835406#4835406
|
||||
function escapeHtml(text) {
|
||||
var map = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
"\"": """,
|
||||
"\'": "'"
|
||||
};
|
||||
|
||||
return text.replace(/[&<>"']/g, function(m) { return map[m]; });
|
||||
}
|
||||
|
||||
function padNumber(num) {
|
||||
return ("00" + num).substr(-2,2);
|
||||
}
|
||||
|
||||
// Helper function needed for converting the Objects to Arrays
|
||||
|
||||
function objectToArray(p){
|
||||
var keys = Object.keys(p);
|
||||
keys.sort(function(a, b) {
|
||||
return a - b;
|
||||
});
|
||||
|
||||
var arr = [], idx = [];
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
arr.push(p[keys[i]]);
|
||||
idx.push(keys[i]);
|
||||
}
|
||||
return [idx,arr];
|
||||
}
|
||||
|
||||
var timeLineChart;
|
||||
|
||||
function compareNumbers(a, b) {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
function updateQueriesOverTime() {
|
||||
$("#queries-over-time .overlay").show();
|
||||
$.getJSON("api_db.php?getGraphData&from="+from+"&until="+until, function(data) {
|
||||
|
||||
// convert received objects to arrays
|
||||
data.domains_over_time = objectToArray(data.domains_over_time);
|
||||
data.ads_over_time = objectToArray(data.ads_over_time);
|
||||
// Remove possibly already existing data
|
||||
timeLineChart.data.labels = [];
|
||||
timeLineChart.data.datasets[0].data = [];
|
||||
timeLineChart.data.datasets[1].data = [];
|
||||
|
||||
var dates = [], hour;
|
||||
|
||||
for (hour in data.domains_over_time[0]) {
|
||||
if ({}.hasOwnProperty.call(data.domains_over_time[0], hour)) {
|
||||
dates.push(parseInt(data.domains_over_time[0][hour]));
|
||||
}
|
||||
}
|
||||
|
||||
for (hour in data.ads_over_time[0]) {
|
||||
if ({}.hasOwnProperty.call(data.ads_over_time[0], hour)) {
|
||||
if(!dates.includes(parseInt(data.ads_over_time[0][hour])))
|
||||
{
|
||||
dates.push(parseInt(data.ads_over_time[0][hour]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dates.sort(compareNumbers);
|
||||
|
||||
// Add data for each hour that is available
|
||||
for (hour in dates) {
|
||||
if ({}.hasOwnProperty.call(dates, hour)) {
|
||||
var d, dom = 0, ads = 0;
|
||||
d = new Date(1000*dates[hour]);
|
||||
|
||||
var idx = data.domains_over_time[0].indexOf(dates[hour].toString());
|
||||
if (idx > -1)
|
||||
{
|
||||
dom = data.domains_over_time[1][idx];
|
||||
}
|
||||
|
||||
idx = data.ads_over_time[0].indexOf(dates[hour].toString());
|
||||
if (idx > -1)
|
||||
{
|
||||
ads = data.ads_over_time[1][idx];
|
||||
}
|
||||
|
||||
timeLineChart.data.labels.push(d);
|
||||
timeLineChart.data.datasets[0].data.push(dom);
|
||||
timeLineChart.data.datasets[1].data.push(ads);
|
||||
}
|
||||
}
|
||||
|
||||
timeLineChart.options.scales.xAxes[0].display=true;
|
||||
$("#queries-over-time .overlay").hide();
|
||||
timeLineChart.update();
|
||||
});
|
||||
}
|
||||
|
||||
/* global Chart */
|
||||
$(document).ready(function() {
|
||||
var ctx = document.getElementById("queryOverTimeChart").getContext("2d");
|
||||
timeLineChart = new Chart(ctx, {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: [ 0 ],
|
||||
datasets: [
|
||||
{
|
||||
label: "Total DNS Queries",
|
||||
fill: true,
|
||||
backgroundColor: "rgba(220,220,220,0.5)",
|
||||
borderColor: "rgba(0, 166, 90,.8)",
|
||||
pointBorderColor: "rgba(0, 166, 90,.8)",
|
||||
pointRadius: 1,
|
||||
pointHoverRadius: 5,
|
||||
data: [],
|
||||
pointHitRadius: 5,
|
||||
cubicInterpolationMode: "monotone"
|
||||
},
|
||||
{
|
||||
label: "Blocked DNS Queries",
|
||||
fill: true,
|
||||
backgroundColor: "rgba(0,192,239,0.5)",
|
||||
borderColor: "rgba(0,192,239,1)",
|
||||
pointBorderColor: "rgba(0,192,239,1)",
|
||||
pointRadius: 1,
|
||||
pointHoverRadius: 5,
|
||||
data: [],
|
||||
pointHitRadius: 5,
|
||||
cubicInterpolationMode: "monotone"
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
enabled: true,
|
||||
responsive: true,
|
||||
mode: "x-axis",
|
||||
callbacks: {
|
||||
title: function(tooltipItem, data) {
|
||||
var label = tooltipItem[0].xLabel;
|
||||
var time = new Date(label);
|
||||
var date = time.getFullYear()+"-"+padNumber(time.getMonth())+"-"+padNumber(time.getDate());
|
||||
var h = time.getHours();
|
||||
var m = time.getMinutes();
|
||||
var from = padNumber(h)+":"+padNumber(m)+":00";
|
||||
var to = padNumber(h)+":"+padNumber(m+9)+":59";
|
||||
return "Queries from "+from+" to "+to+" on "+date;
|
||||
},
|
||||
label: function(tooltipItems, data) {
|
||||
if(tooltipItems.datasetIndex === 1)
|
||||
{
|
||||
var percentage = 0.0;
|
||||
var total = parseInt(data.datasets[0].data[tooltipItems.index]);
|
||||
var blocked = parseInt(data.datasets[1].data[tooltipItems.index]);
|
||||
if(total > 0)
|
||||
{
|
||||
percentage = 100.0*blocked/total;
|
||||
}
|
||||
return data.datasets[tooltipItems.datasetIndex].label + ": " + tooltipItems.yLabel + " (" + percentage.toFixed(1) + "%)";
|
||||
}
|
||||
else
|
||||
{
|
||||
return data.datasets[tooltipItems.datasetIndex].label + ": " + tooltipItems.yLabel;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: "time",
|
||||
display: false,
|
||||
time: {
|
||||
displayFormats: {
|
||||
"minute": "HH:mm",
|
||||
"hour": "HH:mm",
|
||||
"day": "HH:mm",
|
||||
"week": "MMM DD HH:mm",
|
||||
"month": "MMM DD",
|
||||
"quarter": "MMM DD",
|
||||
"year": "MMM DD"
|
||||
}
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}]
|
||||
},
|
||||
maintainAspectRatio: false
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$("#querytime").on("apply.daterangepicker", function(ev, picker) {
|
||||
$("#queries-over-time").show();
|
||||
updateQueriesOverTime();
|
||||
});
|
||||
|
||||
$("#queryOverTimeChart").click(function(evt){
|
||||
var activePoints = timeLineChart.getElementAtEvent(evt);
|
||||
if(activePoints.length > 0)
|
||||
{
|
||||
//get the internal index in the chart
|
||||
var clickedElementindex = activePoints[0]["_index"];
|
||||
|
||||
//get specific label by index
|
||||
var label = timeLineChart.data.labels[clickedElementindex];
|
||||
|
||||
//get value by index
|
||||
var from = label/1000;
|
||||
var until = label/1000 + 600;
|
||||
window.location.href = "db_queries.php?from="+from+"&until="+until;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
@@ -0,0 +1,181 @@
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/*global
|
||||
moment
|
||||
*/
|
||||
|
||||
var start__ = moment().subtract(6, "days");
|
||||
var from = moment(start__).utc().valueOf()/1000;
|
||||
var end__ = moment();
|
||||
var until = moment(end__).utc().valueOf()/1000;
|
||||
|
||||
$(function () {
|
||||
$("#querytime").daterangepicker(
|
||||
{
|
||||
timePicker: true, timePickerIncrement: 15,
|
||||
locale: { format: "MMMM Do YYYY, HH:mm" },
|
||||
ranges: {
|
||||
"Today": [moment().startOf("day"), moment()],
|
||||
"Yesterday": [moment().subtract(1, "days").startOf("day"), moment().subtract(1, "days").endOf("day")],
|
||||
"Last 7 Days": [moment().subtract(6, "days"), moment()],
|
||||
"Last 30 Days": [moment().subtract(29, "days"), moment()],
|
||||
"This Month": [moment().startOf("month"), moment()],
|
||||
"Last Month": [moment().subtract(1, "month").startOf("month"), moment().subtract(1, "month").endOf("month")],
|
||||
"This Year": [moment().startOf("year"), moment()],
|
||||
"All Time": [moment(0), moment()]
|
||||
},
|
||||
"opens": "center", "showDropdowns": true
|
||||
},
|
||||
function (startt, endt) {
|
||||
from = moment(startt).utc().valueOf()/1000;
|
||||
until = moment(endt).utc().valueOf()/1000;
|
||||
});
|
||||
});
|
||||
|
||||
// Credit: http://stackoverflow.com/questions/1787322/htmlspecialchars-equivalent-in-javascript/4835406#4835406
|
||||
function escapeHtml(text) {
|
||||
var map = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
"\"": """,
|
||||
"\'": "'"
|
||||
};
|
||||
|
||||
return text.replace(/[&<>"']/g, function(m) { return map[m]; });
|
||||
}
|
||||
|
||||
function updateTopClientsChart() {
|
||||
$("#client-frequency .overlay").show();
|
||||
$.getJSON("api_db.php?topClients&from="+from+"&until="+until, function(data) {
|
||||
|
||||
// Clear tables before filling them with data
|
||||
$("#client-frequency td").parent().remove();
|
||||
var clienttable = $("#client-frequency").find("tbody:last");
|
||||
var client, percentage, clientname, clientip;
|
||||
var sum = 0;
|
||||
for (client in data.top_sources) {
|
||||
if ({}.hasOwnProperty.call(data.top_sources, client)){
|
||||
sum += data.top_sources[client];
|
||||
}
|
||||
}
|
||||
|
||||
for (client in data.top_sources) {
|
||||
|
||||
if ({}.hasOwnProperty.call(data.top_sources, client)){
|
||||
// Sanitize client
|
||||
client = escapeHtml(client);
|
||||
if(escapeHtml(client) !== client)
|
||||
{
|
||||
// Make a copy with the escaped index if necessary
|
||||
data.top_sources[escapeHtml(client)] = data.top_sources[client];
|
||||
}
|
||||
if(client.indexOf("|") > -1)
|
||||
{
|
||||
var idx = client.indexOf("|");
|
||||
clientname = client.substr(0, idx);
|
||||
clientip = client.substr(idx+1, client.length-idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
clientname = client;
|
||||
clientip = client;
|
||||
}
|
||||
|
||||
var url = clientname;
|
||||
percentage = data.top_sources[client] / sum * 100.0;
|
||||
clienttable.append("<tr> <td>" + url +
|
||||
"</td> <td>" + data.top_sources[client] + "</td> <td> <div class=\"progress progress-sm\" title=\""+percentage.toFixed(1)+"% of " + data.dns_queries_today + "\"> <div class=\"progress-bar progress-bar-blue\" style=\"width: " +
|
||||
percentage + "%\"></div> </div> </td> </tr> ");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$("#client-frequency .overlay").hide();
|
||||
});
|
||||
}
|
||||
|
||||
function updateTopDomainsChart() {
|
||||
$("#domain-frequency .overlay").show();
|
||||
$.getJSON("api_db.php?topDomains&from="+from+"&until="+until, function(data) {
|
||||
|
||||
// Clear tables before filling them with data
|
||||
$("#domain-frequency td").parent().remove();
|
||||
var domaintable = $("#domain-frequency").find("tbody:last");
|
||||
var domain, percentage;
|
||||
var sum = 0;
|
||||
for (domain in data.top_domains) {
|
||||
if ({}.hasOwnProperty.call(data.top_domains, domain)){
|
||||
sum += data.top_domains[domain];
|
||||
}
|
||||
}
|
||||
|
||||
for (domain in data.top_domains) {
|
||||
|
||||
if ({}.hasOwnProperty.call(data.top_domains, domain)){
|
||||
// Sanitize domain
|
||||
domain = escapeHtml(domain);
|
||||
if(escapeHtml(domain) !== domain)
|
||||
{
|
||||
// Make a copy with the escaped index if necessary
|
||||
data.top_domains[escapeHtml(domain)] = data.top_domains[domain];
|
||||
}
|
||||
|
||||
percentage = data.top_domains[domain] / sum * 100.0;
|
||||
domaintable.append("<tr> <td>" + domain +
|
||||
"</td> <td>" + data.top_domains[domain] + "</td> <td> <div class=\"progress progress-sm\" title=\""+percentage.toFixed(1)+"% of " + data.dns_queries_today + "\"> <div class=\"progress-bar progress-bar-blue\" style=\"width: " +
|
||||
percentage + "%\"></div> </div> </td> </tr> ");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$("#domain-frequency .overlay").hide();
|
||||
});
|
||||
}
|
||||
|
||||
function updateTopAdsChart() {
|
||||
$("#ad-frequency .overlay").show();
|
||||
$.getJSON("api_db.php?topAds&from="+from+"&until="+until, function(data) {
|
||||
|
||||
// Clear tables before filling them with data
|
||||
$("#ad-frequency td").parent().remove();
|
||||
var adtable = $("#ad-frequency").find("tbody:last");
|
||||
var ad, percentage;
|
||||
var sum = 0;
|
||||
for (ad in data.top_ads) {
|
||||
if ({}.hasOwnProperty.call(data.top_ads, ad)){
|
||||
sum += data.top_ads[ad];
|
||||
}
|
||||
}
|
||||
|
||||
for (ad in data.top_ads) {
|
||||
|
||||
if ({}.hasOwnProperty.call(data.top_ads, ad)){
|
||||
// Sanitize ad
|
||||
ad = escapeHtml(ad);
|
||||
if(escapeHtml(ad) !== ad)
|
||||
{
|
||||
// Make a copy with the escaped index if necessary
|
||||
data.top_ads[escapeHtml(ad)] = data.top_ads[ad];
|
||||
}
|
||||
|
||||
percentage = data.top_ads[ad] / sum * 100.0;
|
||||
adtable.append("<tr> <td>" + ad + "</td> <td>" + data.top_ads[ad] + "</td> <td> <div class=\"progress progress-sm\" title=\""+percentage.toFixed(1)+"% of " + data.dns_queries_today + "\"> <div class=\"progress-bar progress-bar-blue\" style=\"width: " + percentage + "%\"></div> </div> </td> </tr> ");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$("#ad-frequency .overlay").hide();
|
||||
});
|
||||
}
|
||||
|
||||
$("#querytime").on("apply.daterangepicker", function(ev, picker) {
|
||||
updateTopClientsChart();
|
||||
updateTopDomainsChart();
|
||||
updateTopAdsChart();
|
||||
});
|
||||
@@ -0,0 +1,275 @@
|
||||
/* Pi-hole: A black hole for Internet advertisements
|
||||
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
* Network-wide ad blocking via your own hardware.
|
||||
*
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/*global
|
||||
moment
|
||||
*/
|
||||
|
||||
var start__ = moment().subtract(6, "days");
|
||||
var from = moment(start__).utc().valueOf()/1000;
|
||||
var end__ = moment();
|
||||
var until = moment(end__).utc().valueOf()/1000;
|
||||
var instantquery = false;
|
||||
var daterange;
|
||||
|
||||
// Do we want to filter queries?
|
||||
var GETDict = {};
|
||||
location.search.substr(1).split("&").forEach(function(item) {GETDict[item.split("=")[0]] = item.split("=")[1];});
|
||||
|
||||
if("from" in GETDict && "until" in GETDict)
|
||||
{
|
||||
from = parseInt(GETDict["from"]);
|
||||
until = parseInt(GETDict["until"]);
|
||||
start__ = moment(1000*from);
|
||||
end__ = moment(1000*until);
|
||||
instantquery = true;
|
||||
}
|
||||
|
||||
$(function () {
|
||||
daterange = $("#querytime").daterangepicker(
|
||||
{
|
||||
timePicker: true, timePickerIncrement: 15,
|
||||
locale: { format: "MMMM Do YYYY, HH:mm" },
|
||||
ranges: {
|
||||
"Today": [moment().startOf("day"), moment()],
|
||||
"Yesterday": [moment().subtract(1, "days").startOf("day"), moment().subtract(1, "days").endOf("day")],
|
||||
"Last 7 Days": [moment().subtract(6, "days"), moment()],
|
||||
"Last 30 Days": [moment().subtract(29, "days"), moment()],
|
||||
"This Month": [moment().startOf("month"), moment()],
|
||||
"Last Month": [moment().subtract(1, "month").startOf("month"), moment().subtract(1, "month").endOf("month")],
|
||||
"This Year": [moment().startOf("year"), moment()],
|
||||
"All Time": [moment(0), moment()]
|
||||
},
|
||||
"opens": "center", "showDropdowns": true
|
||||
},
|
||||
function (startt, endt) {
|
||||
from = moment(startt).utc().valueOf()/1000;
|
||||
until = moment(endt).utc().valueOf()/1000;
|
||||
});
|
||||
});
|
||||
|
||||
var tableApi, statistics;
|
||||
|
||||
function escapeRegex(text) {
|
||||
var map = {
|
||||
"(": "\\(",
|
||||
")": "\\)",
|
||||
".": "\\.",
|
||||
};
|
||||
return text.replace(/[().]/g, function(m) { return map[m]; });
|
||||
}
|
||||
|
||||
function add(domain,list) {
|
||||
var token = $("#token").html();
|
||||
var alInfo = $("#alInfo");
|
||||
var alList = $("#alList");
|
||||
var alDomain = $("#alDomain");
|
||||
alDomain.html(domain);
|
||||
var alSuccess = $("#alSuccess");
|
||||
var alFailure = $("#alFailure");
|
||||
var err = $("#err");
|
||||
|
||||
if(list === "white")
|
||||
{
|
||||
alList.html("Whitelist");
|
||||
}
|
||||
else
|
||||
{
|
||||
alList.html("Blacklist");
|
||||
}
|
||||
|
||||
alInfo.show();
|
||||
alSuccess.hide();
|
||||
alFailure.hide();
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/add.php",
|
||||
method: "post",
|
||||
data: {"domain":domain, "list":list, "token":token},
|
||||
success: function(response) {
|
||||
if (response.indexOf("not a valid argument") >= 0 || response.indexOf("is not a valid domain") >= 0)
|
||||
{
|
||||
alFailure.show();
|
||||
err.html(response);
|
||||
alFailure.delay(4000).fadeOut(2000, function() { alFailure.hide(); });
|
||||
}
|
||||
else
|
||||
{
|
||||
alSuccess.show();
|
||||
alSuccess.delay(1000).fadeOut(2000, function() { alSuccess.hide(); });
|
||||
}
|
||||
alInfo.delay(1000).fadeOut(2000, function() {
|
||||
alInfo.hide();
|
||||
alList.html("");
|
||||
alDomain.html("");
|
||||
});
|
||||
},
|
||||
error: function(jqXHR, exception) {
|
||||
alFailure.show();
|
||||
err.html("");
|
||||
alFailure.delay(1000).fadeOut(2000, function() {
|
||||
alFailure.hide();
|
||||
});
|
||||
alInfo.delay(1000).fadeOut(2000, function() {
|
||||
alInfo.hide();
|
||||
alList.html("");
|
||||
alDomain.html("");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
function handleAjaxError( xhr, textStatus, error ) {
|
||||
if ( textStatus === "timeout" )
|
||||
{
|
||||
alert( "The server took too long to send the data." );
|
||||
}
|
||||
else if(xhr.responseText.indexOf("Connection refused") >= 0)
|
||||
{
|
||||
alert( "An error occured while loading the data: Connection refused. Is FTL running?" );
|
||||
}
|
||||
else
|
||||
{
|
||||
alert( "An unknown error occured while loading the data.\n"+xhr.responseText );
|
||||
}
|
||||
$("#all-queries_processing").hide();
|
||||
tableApi.clear();
|
||||
tableApi.draw();
|
||||
}
|
||||
|
||||
var reloadCallback = function()
|
||||
{
|
||||
statistics = [0,0,0,0];
|
||||
var data = tableApi.rows().data();
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
statistics[0]++;
|
||||
if(data[i][4] === 1)
|
||||
{
|
||||
statistics[2]++;
|
||||
}
|
||||
else if(data[i][4] === 3)
|
||||
{
|
||||
statistics[1]++;
|
||||
}
|
||||
else if(data[i][4] === 4)
|
||||
{
|
||||
statistics[3]++;
|
||||
}
|
||||
}
|
||||
$("h3#dns_queries").text(statistics[0]);
|
||||
$("h3#ads_blocked_exact").text(statistics[2]);
|
||||
$("h3#ads_wildcard_blocked").text(statistics[3]);
|
||||
|
||||
var percent = 0.0;
|
||||
if(statistics[2] + statistics[3] > 0)
|
||||
{
|
||||
percent = 100.0*(statistics[2] + statistics[3]) / statistics[0];
|
||||
}
|
||||
$("h3#ads_percentage_today").text(parseFloat(percent).toFixed(1)+" %");
|
||||
};
|
||||
|
||||
function refreshTableData() {
|
||||
var APIstring = "api_db.php?getAllQueries&from="+from+"&until="+until;
|
||||
statistics = [0,0,0];
|
||||
tableApi.ajax.url(APIstring).load(reloadCallback);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
var status;
|
||||
|
||||
var APIstring;
|
||||
if(instantquery)
|
||||
{
|
||||
APIstring = "api_db.php?getAllQueries&from="+from+"&until="+until;
|
||||
}
|
||||
else
|
||||
{
|
||||
APIstring = "api_db.php?getAllQueries=empty";
|
||||
}
|
||||
|
||||
tableApi = $("#all-queries").DataTable( {
|
||||
"rowCallback": function( row, data, index ){
|
||||
if (data[4] === 1)
|
||||
{
|
||||
$(row).css("color","red");
|
||||
$("td:eq(4)", row).html( "Pi-holed" );
|
||||
$("td:eq(5)", row).html( "<button style=\"color:green; white-space: nowrap;\"><i class=\"fa fa-pencil-square-o\"></i> Whitelist</button>" );
|
||||
// statistics[2]++;
|
||||
}
|
||||
else if (data[4] === 2)
|
||||
{
|
||||
$(row).css("color","green");
|
||||
$("td:eq(4)", row).html( "OK (forwarded)" );
|
||||
$("td:eq(5)", row).html( "<button style=\"color:red; white-space: nowrap;\"><i class=\"fa fa-ban\"></i> Blacklist</button>" );
|
||||
}
|
||||
else if (data[4] === 3)
|
||||
{
|
||||
$(row).css("color","green");
|
||||
$("td:eq(4)", row).html( "OK (cached)" );
|
||||
$("td:eq(5)", row).html( "<button style=\"color:red; white-space: nowrap;\"><i class=\"fa fa-ban\"></i> Blacklist</button>" );
|
||||
// statistics[1]++;
|
||||
|
||||
}
|
||||
else if (data[4] === 4)
|
||||
{
|
||||
$(row).css("color","red");
|
||||
$("td:eq(4)", row).html( "Pi-holed (wildcard)" );
|
||||
$("td:eq(5)", row).html( "" );
|
||||
// statistics[3]++;
|
||||
}
|
||||
else
|
||||
{
|
||||
$("td:eq(4)", row).html( "Unknown ("+data[4]+")" );
|
||||
$("td:eq(5)", row).html( "" );
|
||||
}
|
||||
// statistics[0]++;
|
||||
},
|
||||
dom: "<'row'<'col-sm-12'f>>" +
|
||||
"<'row'<'col-sm-4'l><'col-sm-8'p>>" +
|
||||
"<'row'<'col-sm-12'tr>>" +
|
||||
"<'row'<'col-sm-5'i><'col-sm-7'p>>",
|
||||
"ajax": {"url": APIstring, "error": handleAjaxError },
|
||||
"autoWidth" : false,
|
||||
"processing": true,
|
||||
"deferRender": true,
|
||||
"order" : [[0, "desc"]],
|
||||
"columns": [
|
||||
{ "width" : "20%", "render": function (data, type, full, meta) { if(type === "display"){return moment.unix(data).format("Y-MM-DD HH:mm:ss z");}else{return data;} }},
|
||||
{ "width" : "10%" },
|
||||
{ "width" : "40%" },
|
||||
{ "width" : "10%" },
|
||||
{ "width" : "10%" },
|
||||
{ "width" : "10%" },
|
||||
],
|
||||
"lengthMenu": [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]],
|
||||
"columnDefs": [ {
|
||||
"targets": -1,
|
||||
"data": null,
|
||||
"defaultContent": ""
|
||||
} ],
|
||||
"initComplete": reloadCallback
|
||||
});
|
||||
$("#all-queries tbody").on( "click", "button", function () {
|
||||
var data = tableApi.row( $(this).parents("tr") ).data();
|
||||
if (data[4] === "1")
|
||||
{
|
||||
add(data[2],"white");
|
||||
}
|
||||
else
|
||||
{
|
||||
add(data[2],"black");
|
||||
}
|
||||
} );
|
||||
|
||||
if(instantquery)
|
||||
{
|
||||
daterange.val(start__.format("MMMM Do YYYY, HH:mm") + " - " + end__.format("MMMM Do YYYY, HH:mm"));
|
||||
}
|
||||
} );
|
||||
|
||||
$("#querytime").on("apply.daterangepicker", function(ev, picker) {
|
||||
refreshTableData();
|
||||
});
|
||||
|
||||
+372
-238
@@ -5,7 +5,9 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
// Define global variables
|
||||
/* global Chart */
|
||||
var timeLineChart, queryTypeChart, forwardDestinationChart;
|
||||
var queryTypePieChart, forwardDestinationPieChart;
|
||||
|
||||
function padNumber(num) {
|
||||
return ("00" + num).substr(-2,2);
|
||||
@@ -145,6 +147,48 @@ function updateQueryTypesOverTime() {
|
||||
}
|
||||
});
|
||||
}
|
||||
function updateQueryTypesPie() {
|
||||
$.getJSON("api.php?getQueryTypes", function(data) {
|
||||
|
||||
if("FTLnotrunning" in data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var colors = [];
|
||||
// Get colors from AdminLTE
|
||||
$.each($.AdminLTE.options.colors, function(key, value) { colors.push(value); });
|
||||
var v = [], c = [], k = [], iter;
|
||||
// Collect values and colors, and labels
|
||||
if(data.hasOwnProperty("querytypes"))
|
||||
{
|
||||
iter = data.querytypes;
|
||||
}
|
||||
else
|
||||
{
|
||||
iter = data;
|
||||
}
|
||||
$.each(iter, function(key , value) {
|
||||
v.push(value);
|
||||
c.push(colors.shift());
|
||||
k.push(key);
|
||||
});
|
||||
// Build a single dataset with the data to be pushed
|
||||
var dd = {data: v, backgroundColor: c};
|
||||
// and push it at once
|
||||
queryTypePieChart.data.datasets[0] = dd;
|
||||
queryTypePieChart.data.labels = k;
|
||||
$("#query-types-pie .overlay").hide();
|
||||
queryTypePieChart.update();
|
||||
queryTypePieChart.chart.config.options.cutoutPercentage=50;
|
||||
queryTypePieChart.update();
|
||||
// Don't use rotation animation for further updates
|
||||
queryTypePieChart.options.animation.duration=0;
|
||||
}).done(function() {
|
||||
// Reload graph after minute
|
||||
setTimeout(updateQueryTypesPie, 60000);
|
||||
});
|
||||
}
|
||||
|
||||
function updateForwardedOverTime() {
|
||||
$.getJSON("api.php?overTimeDataForwards&getForwardDestinationNames", function(data) {
|
||||
@@ -233,6 +277,46 @@ function updateForwardedOverTime() {
|
||||
});
|
||||
}
|
||||
|
||||
function updateForwardDestinationsPie() {
|
||||
$.getJSON("api.php?getForwardDestinations=unsorted", function(data) {
|
||||
|
||||
if("FTLnotrunning" in data)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var colors = [];
|
||||
// Get colors from AdminLTE
|
||||
$.each($.AdminLTE.options.colors, function(key, value) { colors.push(value); });
|
||||
var v = [], c = [], k = [];
|
||||
// Collect values and colors, immediately push individual labels
|
||||
$.each(data.forward_destinations, function(key , value) {
|
||||
v.push(value);
|
||||
c.push(colors.shift());
|
||||
if(key.indexOf("|") > -1)
|
||||
{
|
||||
key = key.substr(0, key.indexOf("|"));
|
||||
}
|
||||
k.push(key);
|
||||
});
|
||||
// Build a single dataset with the data to be pushed
|
||||
var dd = {data: v, backgroundColor: c};
|
||||
// and push it at once
|
||||
forwardDestinationPieChart.data.labels = k;
|
||||
forwardDestinationPieChart.data.datasets[0] = dd;
|
||||
// and push it at once
|
||||
$("#forward-destinations-pie .overlay").hide();
|
||||
forwardDestinationPieChart.update();
|
||||
forwardDestinationPieChart.chart.config.options.cutoutPercentage=50;
|
||||
forwardDestinationPieChart.update();
|
||||
// Don't use rotation animation for further updates
|
||||
forwardDestinationPieChart.options.animation.duration=0;
|
||||
}).done(function() {
|
||||
// Reload graph after one minute
|
||||
setTimeout(updateForwardDestinationsPie, 60000);
|
||||
});
|
||||
}
|
||||
|
||||
// Credit: http://stackoverflow.com/questions/1787322/htmlspecialchars-equivalent-in-javascript/4835406#4835406
|
||||
function escapeHtml(text) {
|
||||
var map = {
|
||||
@@ -375,8 +459,8 @@ function updateSummaryData(runOnce) {
|
||||
|
||||
if("FTLnotrunning" in data)
|
||||
{
|
||||
data["ads_blocked_today"] = "Lost";
|
||||
data["dns_queries_today"] = "connection";
|
||||
data["dns_queries_today"] = "Lost";
|
||||
data["ads_blocked_today"] = "connection";
|
||||
data["ads_percentage_today"] = "to";
|
||||
data["domains_being_blocked"] = "API";
|
||||
// Adjust text
|
||||
@@ -406,19 +490,19 @@ function updateSummaryData(runOnce) {
|
||||
}
|
||||
}
|
||||
|
||||
["ads_blocked_today", "dns_queries_today", "ads_percentage_today"].forEach(function(today) {
|
||||
var todayElement = $("h3#" + today);
|
||||
["ads_blocked_today", "dns_queries_today", "ads_percentage_today", "unique_clients"].forEach(function(today) {
|
||||
var todayElement = $("span#" + today);
|
||||
todayElement.text() !== data[today] &&
|
||||
todayElement.text() !== data[today] + "%" &&
|
||||
todayElement.addClass("glow");
|
||||
$("span#" + today).addClass("glow");
|
||||
});
|
||||
|
||||
window.setTimeout(function() {
|
||||
["ads_blocked_today", "dns_queries_today", "domains_being_blocked", "ads_percentage_today"].forEach(function(header, idx) {
|
||||
["ads_blocked_today", "dns_queries_today", "domains_being_blocked", "ads_percentage_today", "unique_clients"].forEach(function(header, idx) {
|
||||
var textData = (idx === 3 && data[header] !== "to") ? data[header] + "%" : data[header];
|
||||
$("h3#" + header).text(textData);
|
||||
$("span#" + header).text(textData);
|
||||
});
|
||||
$("h3.statistic.glow").removeClass("glow");
|
||||
$("span.glow").removeClass("glow");
|
||||
}, 500);
|
||||
|
||||
}).done(function() {
|
||||
@@ -437,59 +521,130 @@ function updateSummaryData(runOnce) {
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
var isMobile = {
|
||||
Windows: function() {
|
||||
return /IEMobile/i.test(navigator.userAgent);
|
||||
},
|
||||
Android: function() {
|
||||
return /Android/i.test(navigator.userAgent);
|
||||
},
|
||||
BlackBerry: function() {
|
||||
return /BlackBerry/i.test(navigator.userAgent);
|
||||
},
|
||||
iOS: function() {
|
||||
return /iPhone|iPad|iPod/i.test(navigator.userAgent);
|
||||
},
|
||||
any: function() {
|
||||
return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Windows());
|
||||
}
|
||||
};
|
||||
var isMobile = {
|
||||
Windows: function() {
|
||||
return /IEMobile/i.test(navigator.userAgent);
|
||||
},
|
||||
Android: function() {
|
||||
return /Android/i.test(navigator.userAgent);
|
||||
},
|
||||
BlackBerry: function() {
|
||||
return /BlackBerry/i.test(navigator.userAgent);
|
||||
},
|
||||
iOS: function() {
|
||||
return /iPhone|iPad|iPod/i.test(navigator.userAgent);
|
||||
},
|
||||
any: function() {
|
||||
return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Windows());
|
||||
}
|
||||
};
|
||||
|
||||
// Pull in data via AJAX
|
||||
// Pull in data via AJAX
|
||||
|
||||
updateSummaryData();
|
||||
updateSummaryData();
|
||||
|
||||
var ctx = document.getElementById("queryOverTimeChart").getContext("2d");
|
||||
timeLineChart = new Chart(ctx, {
|
||||
var ctx = document.getElementById("queryOverTimeChart").getContext("2d");
|
||||
timeLineChart = new Chart(ctx, {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [
|
||||
{
|
||||
label: "Total DNS Queries",
|
||||
fill: true,
|
||||
backgroundColor: "rgba(220,220,220,0.5)",
|
||||
borderColor: "rgba(0, 166, 90,.8)",
|
||||
pointBorderColor: "rgba(0, 166, 90,.8)",
|
||||
pointRadius: 1,
|
||||
pointHoverRadius: 5,
|
||||
data: [],
|
||||
pointHitRadius: 5,
|
||||
cubicInterpolationMode: "monotone"
|
||||
},
|
||||
{
|
||||
label: "Blocked DNS Queries",
|
||||
fill: true,
|
||||
backgroundColor: "rgba(0,192,239,0.5)",
|
||||
borderColor: "rgba(0,192,239,1)",
|
||||
pointBorderColor: "rgba(0,192,239,1)",
|
||||
pointRadius: 1,
|
||||
pointHoverRadius: 5,
|
||||
data: [],
|
||||
pointHitRadius: 5,
|
||||
cubicInterpolationMode: "monotone"
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
enabled: true,
|
||||
mode: "x-axis",
|
||||
callbacks: {
|
||||
title: function(tooltipItem, data) {
|
||||
var label = tooltipItem[0].xLabel;
|
||||
var time = label.match(/(\d?\d):?(\d?\d?)/);
|
||||
var h = parseInt(time[1], 10);
|
||||
var m = parseInt(time[2], 10) || 0;
|
||||
var from = padNumber(h)+":"+padNumber(m-5)+":00";
|
||||
var to = padNumber(h)+":"+padNumber(m+4)+":59";
|
||||
return "Queries from "+from+" to "+to;
|
||||
},
|
||||
label: function(tooltipItems, data) {
|
||||
if(tooltipItems.datasetIndex === 1)
|
||||
{
|
||||
var percentage = 0.0;
|
||||
var total = parseInt(data.datasets[0].data[tooltipItems.index]);
|
||||
var blocked = parseInt(data.datasets[1].data[tooltipItems.index]);
|
||||
if(total > 0)
|
||||
{
|
||||
percentage = 100.0*blocked/total;
|
||||
}
|
||||
return data.datasets[tooltipItems.datasetIndex].label + ": " + tooltipItems.yLabel + " (" + percentage.toFixed(1) + "%)";
|
||||
}
|
||||
else
|
||||
{
|
||||
return data.datasets[tooltipItems.datasetIndex].label + ": " + tooltipItems.yLabel;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: "time",
|
||||
time: {
|
||||
unit: "hour",
|
||||
displayFormats: {
|
||||
hour: "HH:mm"
|
||||
},
|
||||
tooltipFormat: "HH:mm"
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
beginAtZero: true
|
||||
}
|
||||
}]
|
||||
},
|
||||
maintainAspectRatio: false
|
||||
}
|
||||
});
|
||||
|
||||
// Pull in data via AJAX
|
||||
|
||||
updateQueriesOverTime();
|
||||
|
||||
// Create / load "Forward Destinations over Time" only if authorized
|
||||
if(document.getElementById("forwardDestinationChart"))
|
||||
{
|
||||
ctx = document.getElementById("forwardDestinationChart").getContext("2d");
|
||||
forwardDestinationChart = new Chart(ctx, {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [
|
||||
{
|
||||
label: "Total DNS Queries",
|
||||
fill: true,
|
||||
backgroundColor: "rgba(220,220,220,0.5)",
|
||||
borderColor: "rgba(0, 166, 90,.8)",
|
||||
pointBorderColor: "rgba(0, 166, 90,.8)",
|
||||
pointRadius: 1,
|
||||
pointHoverRadius: 5,
|
||||
data: [],
|
||||
pointHitRadius: 5,
|
||||
cubicInterpolationMode: "monotone"
|
||||
},
|
||||
{
|
||||
label: "Blocked DNS Queries",
|
||||
fill: true,
|
||||
backgroundColor: "rgba(0,192,239,0.5)",
|
||||
borderColor: "rgba(0,192,239,1)",
|
||||
pointBorderColor: "rgba(0,192,239,1)",
|
||||
pointRadius: 1,
|
||||
pointHoverRadius: 5,
|
||||
data: [],
|
||||
pointHitRadius: 5,
|
||||
cubicInterpolationMode: "monotone"
|
||||
}
|
||||
]
|
||||
datasets: [{ data: [] }]
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
@@ -503,24 +658,10 @@ $(document).ready(function() {
|
||||
var m = parseInt(time[2], 10) || 0;
|
||||
var from = padNumber(h)+":"+padNumber(m-5)+":00";
|
||||
var to = padNumber(h)+":"+padNumber(m+4)+":59";
|
||||
return "Queries from "+from+" to "+to;
|
||||
return "Forward destinations from "+from+" to "+to;
|
||||
},
|
||||
label: function(tooltipItems, data) {
|
||||
if(tooltipItems.datasetIndex === 1)
|
||||
{
|
||||
var percentage = 0.0;
|
||||
var total = parseInt(data.datasets[0].data[tooltipItems.index]);
|
||||
var blocked = parseInt(data.datasets[1].data[tooltipItems.index]);
|
||||
if(total > 0)
|
||||
{
|
||||
percentage = 100.0*blocked/total;
|
||||
}
|
||||
return data.datasets[tooltipItems.datasetIndex].label + ": " + tooltipItems.yLabel + " (" + percentage.toFixed(1) + "%)";
|
||||
}
|
||||
else
|
||||
{
|
||||
return data.datasets[tooltipItems.datasetIndex].label + ": " + tooltipItems.yLabel;
|
||||
}
|
||||
return data.datasets[tooltipItems.datasetIndex].label + ": " + (100.0*tooltipItems.yLabel).toFixed(1) + "%";
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -540,187 +681,180 @@ $(document).ready(function() {
|
||||
}],
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
beginAtZero: true
|
||||
}
|
||||
mix: 0.0,
|
||||
max: 1.0,
|
||||
beginAtZero: true,
|
||||
callback: function(value, index, values) {
|
||||
return Math.round(value*100) + " %";
|
||||
}
|
||||
},
|
||||
stacked: true
|
||||
}]
|
||||
},
|
||||
maintainAspectRatio: false
|
||||
maintainAspectRatio: true
|
||||
}
|
||||
});
|
||||
|
||||
// Pull in data via AJAX
|
||||
updateForwardedOverTime();
|
||||
}
|
||||
|
||||
updateQueriesOverTime();
|
||||
|
||||
// Create / load "Forward Destinations over Time" only if authorized
|
||||
if(document.getElementById("forwardDestinationChart"))
|
||||
{
|
||||
ctx = document.getElementById("forwardDestinationChart").getContext("2d");
|
||||
forwardDestinationChart = new Chart(ctx, {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{ data: [] }]
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
enabled: true,
|
||||
mode: "x-axis",
|
||||
callbacks: {
|
||||
title: function(tooltipItem, data) {
|
||||
var label = tooltipItem[0].xLabel;
|
||||
var time = label.match(/(\d?\d):?(\d?\d?)/);
|
||||
var h = parseInt(time[1], 10);
|
||||
var m = parseInt(time[2], 10) || 0;
|
||||
var from = padNumber(h)+":"+padNumber(m-5)+":00";
|
||||
var to = padNumber(h)+":"+padNumber(m+4)+":59";
|
||||
return "Forward destinations from "+from+" to "+to;
|
||||
},
|
||||
label: function(tooltipItems, data) {
|
||||
return data.datasets[tooltipItems.datasetIndex].label + ": " + (100.0*tooltipItems.yLabel).toFixed(1) + "%";
|
||||
}
|
||||
}
|
||||
// Create / load "Query Types over Time" only if authorized
|
||||
if(document.getElementById("queryTypeChart"))
|
||||
{
|
||||
ctx = document.getElementById("queryTypeChart").getContext("2d");
|
||||
queryTypeChart = new Chart(ctx, {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [
|
||||
{
|
||||
label: "A: IPv4 queries",
|
||||
pointRadius: 0,
|
||||
pointHitRadius: 5,
|
||||
pointHoverRadius: 5,
|
||||
data: []
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: "time",
|
||||
time: {
|
||||
unit: "hour",
|
||||
displayFormats: {
|
||||
hour: "HH:mm"
|
||||
},
|
||||
tooltipFormat: "HH:mm"
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
mix: 0.0,
|
||||
max: 1.0,
|
||||
beginAtZero: true,
|
||||
callback: function(value, index, values) {
|
||||
return Math.round(value*100) + " %";
|
||||
}
|
||||
},
|
||||
stacked: true
|
||||
}]
|
||||
},
|
||||
maintainAspectRatio: true
|
||||
}
|
||||
});
|
||||
|
||||
// Pull in data via AJAX
|
||||
updateForwardedOverTime();
|
||||
}
|
||||
|
||||
// Create / load "Query Types over Time" only if authorized
|
||||
if(document.getElementById("queryTypeChart"))
|
||||
{
|
||||
ctx = document.getElementById("queryTypeChart").getContext("2d");
|
||||
queryTypeChart = new Chart(ctx, {
|
||||
type: "line",
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [
|
||||
{
|
||||
label: "A: IPv4 queries",
|
||||
pointRadius: 0,
|
||||
pointHitRadius: 5,
|
||||
pointHoverRadius: 5,
|
||||
data: []
|
||||
{
|
||||
label: "AAAA: IPv6 queries",
|
||||
pointRadius: 0,
|
||||
pointHitRadius: 5,
|
||||
pointHoverRadius: 5,
|
||||
data: []
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
enabled: true,
|
||||
mode: "x-axis",
|
||||
callbacks: {
|
||||
title: function(tooltipItem, data) {
|
||||
var label = tooltipItem[0].xLabel;
|
||||
var time = label.match(/(\d?\d):?(\d?\d?)/);
|
||||
var h = parseInt(time[1], 10);
|
||||
var m = parseInt(time[2], 10) || 0;
|
||||
var from = padNumber(h)+":"+padNumber(m-5)+":00";
|
||||
var to = padNumber(h)+":"+padNumber(m+4)+":59";
|
||||
return "Query types from "+from+" to "+to;
|
||||
},
|
||||
{
|
||||
label: "AAAA: IPv6 queries",
|
||||
pointRadius: 0,
|
||||
pointHitRadius: 5,
|
||||
pointHoverRadius: 5,
|
||||
data: []
|
||||
label: function(tooltipItems, data) {
|
||||
return data.datasets[tooltipItems.datasetIndex].label + ": " + (100.0*tooltipItems.yLabel).toFixed(1) + "%";
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
options: {
|
||||
tooltips: {
|
||||
enabled: true,
|
||||
mode: "x-axis",
|
||||
callbacks: {
|
||||
title: function(tooltipItem, data) {
|
||||
var label = tooltipItem[0].xLabel;
|
||||
var time = label.match(/(\d?\d):?(\d?\d?)/);
|
||||
var h = parseInt(time[1], 10);
|
||||
var m = parseInt(time[2], 10) || 0;
|
||||
var from = padNumber(h)+":"+padNumber(m-5)+":00";
|
||||
var to = padNumber(h)+":"+padNumber(m+4)+":59";
|
||||
return "Query types from "+from+" to "+to;
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: "time",
|
||||
time: {
|
||||
unit: "hour",
|
||||
displayFormats: {
|
||||
hour: "HH:mm"
|
||||
},
|
||||
label: function(tooltipItems, data) {
|
||||
return data.datasets[tooltipItems.datasetIndex].label + ": " + (100.0*tooltipItems.yLabel).toFixed(1) + "%";
|
||||
}
|
||||
tooltipFormat: "HH:mm"
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
scales: {
|
||||
xAxes: [{
|
||||
type: "time",
|
||||
time: {
|
||||
unit: "hour",
|
||||
displayFormats: {
|
||||
hour: "HH:mm"
|
||||
},
|
||||
tooltipFormat: "HH:mm"
|
||||
}],
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
mix: 0.0,
|
||||
max: 1.0,
|
||||
beginAtZero: true,
|
||||
callback: function(value, index, values) {
|
||||
return Math.round(value*100) + " %";
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
ticks: {
|
||||
mix: 0.0,
|
||||
max: 1.0,
|
||||
beginAtZero: true,
|
||||
callback: function(value, index, values) {
|
||||
return Math.round(value*100) + " %";
|
||||
}
|
||||
},
|
||||
stacked: true
|
||||
}]
|
||||
},
|
||||
maintainAspectRatio: true
|
||||
}
|
||||
});
|
||||
|
||||
// Pull in data via AJAX
|
||||
updateQueryTypesOverTime();
|
||||
}
|
||||
|
||||
// Create / load "Top Domains" and "Top Advertisers" only if authorized
|
||||
if(document.getElementById("domain-frequency")
|
||||
&& document.getElementById("ad-frequency"))
|
||||
{
|
||||
updateTopLists();
|
||||
}
|
||||
|
||||
// Create / load "Top Clients" only if authorized
|
||||
if(document.getElementById("client-frequency"))
|
||||
{
|
||||
updateTopClientsChart();
|
||||
}
|
||||
|
||||
$("#queryOverTimeChart").click(function(evt){
|
||||
var activePoints = timeLineChart.getElementAtEvent(evt);
|
||||
if(activePoints.length > 0)
|
||||
{
|
||||
//get the internal index of slice in pie chart
|
||||
var clickedElementindex = activePoints[0]["_index"];
|
||||
|
||||
//get specific label by index
|
||||
var label = timeLineChart.data.labels[clickedElementindex];
|
||||
|
||||
//get value by index
|
||||
var from = label/1000 - 300;
|
||||
var until = label/1000 + 300;
|
||||
window.location.href = "queries.php?from="+from+"&until="+until;
|
||||
},
|
||||
stacked: true
|
||||
}]
|
||||
},
|
||||
maintainAspectRatio: true
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Pull in data via AJAX
|
||||
updateQueryTypesOverTime();
|
||||
}
|
||||
|
||||
// Create / load "Top Domains" and "Top Advertisers" only if authorized
|
||||
if(document.getElementById("domain-frequency")
|
||||
&& document.getElementById("ad-frequency"))
|
||||
{
|
||||
updateTopLists();
|
||||
}
|
||||
|
||||
// Create / load "Top Clients" only if authorized
|
||||
if(document.getElementById("client-frequency"))
|
||||
{
|
||||
updateTopClientsChart();
|
||||
}
|
||||
|
||||
$("#queryOverTimeChart").click(function(evt){
|
||||
var activePoints = timeLineChart.getElementAtEvent(evt);
|
||||
if(activePoints.length > 0)
|
||||
{
|
||||
//get the internal index of slice in pie chart
|
||||
var clickedElementindex = activePoints[0]["_index"];
|
||||
|
||||
//get specific label by index
|
||||
var label = timeLineChart.data.labels[clickedElementindex];
|
||||
|
||||
//get value by index
|
||||
var from = label/1000 - 300;
|
||||
var until = label/1000 + 300;
|
||||
window.location.href = "queries.php?from="+from+"&until="+until;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if(document.getElementById("queryTypePieChart"))
|
||||
{
|
||||
ctx = document.getElementById("queryTypePieChart").getContext("2d");
|
||||
queryTypePieChart = new Chart(ctx, {
|
||||
type: "doughnut",
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{ data: [] }]
|
||||
},
|
||||
options: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: "right"
|
||||
},
|
||||
animation: {
|
||||
duration: 2000
|
||||
},
|
||||
cutoutPercentage: 0
|
||||
}
|
||||
});
|
||||
|
||||
// Pull in data via AJAX
|
||||
updateQueryTypesPie();
|
||||
}
|
||||
|
||||
if(document.getElementById("forwardDestinationPieChart"))
|
||||
{
|
||||
ctx = document.getElementById("forwardDestinationPieChart").getContext("2d");
|
||||
forwardDestinationPieChart = new Chart(ctx, {
|
||||
type: "doughnut",
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [{ data: [] }]
|
||||
},
|
||||
options: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: "right"
|
||||
},
|
||||
animation: {
|
||||
duration: 2000
|
||||
},
|
||||
cutoutPercentage: 0
|
||||
}
|
||||
});
|
||||
|
||||
// Pull in data via AJAX
|
||||
updateForwardDestinationsPie();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -175,8 +175,8 @@ $(document).ready(function() {
|
||||
"columns": [
|
||||
{ "width" : "20%", "render": function (data, type, full, meta) { if(type === "display"){return moment.unix(data).format("Y-MM-DD HH:mm:ss z");}else{return data;} }},
|
||||
{ "width" : "10%" },
|
||||
{ "width" : "40%" },
|
||||
{ "width" : "10%" },
|
||||
{ "width" : "40%", "render": $.fn.dataTable.render.text() },
|
||||
{ "width" : "10%", "render": $.fn.dataTable.render.text() },
|
||||
{ "width" : "10%" },
|
||||
{ "width" : "10%" },
|
||||
],
|
||||
|
||||
@@ -16,13 +16,33 @@ list_verify($type);
|
||||
|
||||
switch($type) {
|
||||
case "white":
|
||||
echo exec("sudo pihole -w -q ${_POST['domain']}");
|
||||
if(!isset($_POST["auditlog"]))
|
||||
echo exec("sudo pihole -w -q ${_POST['domain']}");
|
||||
else
|
||||
{
|
||||
echo exec("sudo pihole -w -q -n ${_POST['domain']}");
|
||||
echo exec("sudo pihole -a audit ${_POST['domain']}");
|
||||
}
|
||||
break;
|
||||
case "black":
|
||||
echo exec("sudo pihole -b -q ${_POST['domain']}");
|
||||
if(!isset($_POST["auditlog"]))
|
||||
echo exec("sudo pihole -b -q ${_POST['domain']}");
|
||||
else
|
||||
{
|
||||
echo exec("sudo pihole -b -q -n ${_POST['domain']}");
|
||||
echo exec("sudo pihole -a audit ${_POST['domain']}");
|
||||
}
|
||||
break;
|
||||
case "wild":
|
||||
echo exec("sudo pihole -wild -q ${_POST['domain']}");
|
||||
if(!isset($_POST["auditlog"]))
|
||||
echo exec("sudo pihole -wild -q ${_POST['domain']}");
|
||||
else
|
||||
{
|
||||
echo exec("sudo pihole -wild -q -n ${_POST['domain']}");
|
||||
echo exec("sudo pihole -a audit ${_POST['domain']}");
|
||||
}
|
||||
case "audit":
|
||||
echo exec("sudo pihole -a audit ${_POST['domain']}");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
// Flushes the system write buffers of PHP. This attempts to push everything we have so far all the way to the client's browser.
|
||||
flush();
|
||||
// Run update checker
|
||||
// - determines local branch each time,
|
||||
// - determines local and remote version every 30 minutes
|
||||
require "scripts/pi-hole/php/update_checker.php";
|
||||
?>
|
||||
<!-- /.content-wrapper -->
|
||||
<footer class="main-footer">
|
||||
<!-- Version Infos -->
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
|
||||
function is_valid_domain_name($domain_name)
|
||||
{
|
||||
return (preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $domain_name) && //valid chars check
|
||||
preg_match("/^.{1,253}$/", $domain_name) && //overall length check
|
||||
preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name)); //length of each label
|
||||
return (preg_match("/^((-|_)*[a-z\d]((-|_)*[a-z\d])*(-|_)*)(\.(-|_)*([a-z\d]((-|_)*[a-z\d])*))*$/i", $domain_name) && // Valid chars check
|
||||
preg_match("/^.{1,253}$/", $domain_name) && // Overall length check
|
||||
preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name)); // Length of each label
|
||||
}
|
||||
|
||||
function checkfile($filename) {
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
<?php
|
||||
require "scripts/pi-hole/php/auth.php";
|
||||
require "scripts/pi-hole/php/password.php";
|
||||
require "scripts/pi-hole/php/update_checker.php";
|
||||
|
||||
check_cors();
|
||||
|
||||
@@ -184,6 +183,7 @@
|
||||
<link href="style/vendor/font-awesome-4.5.0/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
|
||||
<link href="style/vendor/ionicons-2.0.1/css/ionicons.min.css" rel="stylesheet" type="text/css" />
|
||||
<link href="style/vendor/dataTables.bootstrap.min.css" rel="stylesheet" type="text/css" />
|
||||
<link href="style/vendor/daterangepicker.css" rel="stylesheet" type="text/css" />
|
||||
|
||||
<link href="style/vendor/AdminLTE.min.css" rel="stylesheet" type="text/css" />
|
||||
<link href="style/vendor/skin-blue.min.css" rel="stylesheet" type="text/css" />
|
||||
@@ -191,7 +191,7 @@
|
||||
<link rel="icon" type="image/png" sizes="160x160" href="img/logo.svg" />
|
||||
<style type="text/css">
|
||||
.glow { text-shadow: 0px 0px 5px #fff; }
|
||||
h3 { transition-duration: 500ms }
|
||||
.small-box span { transition-duration: 500ms }
|
||||
</style>
|
||||
|
||||
<!--[if lt IE 9]>
|
||||
@@ -234,7 +234,7 @@ if($auth) {
|
||||
</a>
|
||||
<div class="navbar-custom-menu">
|
||||
<ul class="nav navbar-nav">
|
||||
<!-- User Account: style can be found in dropdown.less -->
|
||||
<li><a style="pointer-events:none;"><samp><?php echo gethostname(); ?></samp></a></li>
|
||||
<li class="dropdown user user-menu">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" aria-expanded="true">
|
||||
<img src="img/logo.svg" class="user-image" style="border-radius: initial" sizes="160x160" alt="Pi-hole logo" />
|
||||
@@ -267,6 +267,7 @@ if($auth) {
|
||||
<!-- Menu Footer -->
|
||||
<li class="user-footer">
|
||||
<!-- Version Infos -->
|
||||
<?php /*
|
||||
<div class="<?php if(!isset($core_commit) && !isset($web_commit)) { ?>hidden-md <?php } ?>hidden-lg">
|
||||
<b>Pi-hole Version </b> <?php
|
||||
echo $core_current;
|
||||
@@ -280,6 +281,7 @@ if($auth) {
|
||||
echo $FTL_current;
|
||||
if($FTL_update){ ?> <a class="alert-link lookatme btn-link" href="https://github.com/pi-hole/FTL/releases" target="_blank" style="background:none">(Update available!)</a><?php } ?><br><br>
|
||||
</div>
|
||||
*/ ?>
|
||||
<!-- PayPal -->
|
||||
<div class="text-center">
|
||||
<a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3J2L3Z4DHW9UY" target="_blank" style="background:none">
|
||||
@@ -416,6 +418,31 @@ if($auth) {
|
||||
<i class="fa fa-file-text-o"></i> <span>Query Log</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="treeview<?php if($scriptname === "db_queries.php" || $scriptname === "db_lists.php" || $scriptname === "db_graph.php"){ ?> active<?php } ?>">
|
||||
<a href="#">
|
||||
<i class="fa fa-clock-o"></i> <span>Long term data</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-down pull-right" style="padding-right: 5px;"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<li<?php if($scriptname === "db_graph.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="db_graph.php">
|
||||
<i class="fa fa-file-text-o"></i> <span>Graphics</span>
|
||||
</a>
|
||||
</li>
|
||||
<li<?php if($scriptname === "db_queries.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="db_queries.php">
|
||||
<i class="fa fa-file-text-o"></i> <span>Query Log</span>
|
||||
</a>
|
||||
</li>
|
||||
<li<?php if($scriptname === "db_lists.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="db_lists.php">
|
||||
<i class="fa fa-file-text-o"></i> <span>Top Lists</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<!-- Whitelist -->
|
||||
<li<?php if($scriptname === "whitelist"){ ?> class="active"<?php } ?>>
|
||||
<a href="list.php?l=white">
|
||||
@@ -470,7 +497,7 @@ if($auth) {
|
||||
<a href="#"><i class="fa fa-play"></i> <span id="enableLabel">Enable</span> <span id="flip-status-enable"></span></a>
|
||||
</li>
|
||||
<!-- Tools -->
|
||||
<li class="treeview <?php if($scriptname === "gravity.php" || $scriptname === "queryads.php" || $scriptname === "debug.php"){ ?>active<?php } ?>">
|
||||
<li class="treeview <?php if(in_array($scriptname, array("gravity.php", "queryads.php", "auditlog.php", "taillog.php", "taillog-FTL.php", "debug.php"))){ ?>active<?php } ?>">
|
||||
<a href="#">
|
||||
<i class="fa fa-folder"></i> <span>Tools</span>
|
||||
<span class="pull-right-container">
|
||||
@@ -490,6 +517,12 @@ if($auth) {
|
||||
<i class="fa fa-search"></i> <span>Query adlists</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Audit log -->
|
||||
<li<?php if($scriptname === "auditlog.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="auditlog.php">
|
||||
<i class="fa fa-balance-scale"></i> <span>Audit log</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Tail pihole.log -->
|
||||
<li<?php if($scriptname === "taillog.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="taillog.php">
|
||||
|
||||
@@ -22,9 +22,9 @@ function echoEvent($datatext) {
|
||||
// Credit: http://stackoverflow.com/a/4694816/2087442
|
||||
function is_valid_domain_name($domain_name)
|
||||
{
|
||||
return (preg_match("/^([a-z\d](-*[a-z\d])*)(\.([a-z\d](-*[a-z\d])*))*$/i", $domain_name) //valid chars check
|
||||
&& preg_match("/^.{1,253}$/", $domain_name) //overall length check
|
||||
&& preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name) ); //length of each label
|
||||
return (preg_match("/^((-|_)*[a-z\d]((-|_)*[a-z\d])*(-|_)*)(\.(-|_)*([a-z\d]((-|_)*[a-z\d])*))*$/i", $domain_name) // Valid chars check
|
||||
&& preg_match("/^.{1,253}$/", $domain_name) // Overall length check
|
||||
&& preg_match("/^[^\.]{1,63}(\.[^\.]{1,63})*$/", $domain_name) ); // Length of each label
|
||||
}
|
||||
|
||||
// Test if domain is set
|
||||
|
||||
Vendored
+1542
File diff suppressed because it is too large
Load Diff
+6
-1
@@ -9,6 +9,10 @@
|
||||
require "scripts/pi-hole/php/savesettings.php";
|
||||
// Reread ini file as things might have been changed
|
||||
$setupVars = parse_ini_file("/etc/pihole/setupVars.conf");
|
||||
|
||||
function command_exist($cmd) {
|
||||
return !empty(shell_exec(sprintf("which %s", escapeshellarg($cmd))));
|
||||
}
|
||||
?>
|
||||
<style type="text/css">
|
||||
.tooltip-inner {
|
||||
@@ -922,7 +926,8 @@ if($FTL)
|
||||
</form>
|
||||
<?php } else { ?>
|
||||
<p>The PHP extension <code>zip</code> is not loaded. Please ensure it is installed and loaded if you want to use the Pi-hole teleporter.</p>
|
||||
<?php } ?>
|
||||
<?php if(command_exist("apt-get") && command_exist("sudo")) { ?><p>Try:</p><pre>sudo apt-get install php-zip</pre><?php }
|
||||
} ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Vendored
+232
@@ -0,0 +1,232 @@
|
||||
.daterangepicker {
|
||||
position: absolute;
|
||||
color: inherit;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
width: 278px;
|
||||
padding: 4px;
|
||||
margin-top: 1px;
|
||||
top: 100px;
|
||||
left: 20px;
|
||||
/* Calendars */ }
|
||||
.daterangepicker:before, .daterangepicker:after {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||
content: ''; }
|
||||
.daterangepicker:before {
|
||||
top: -7px;
|
||||
border-right: 7px solid transparent;
|
||||
border-left: 7px solid transparent;
|
||||
border-bottom: 7px solid #ccc; }
|
||||
.daterangepicker:after {
|
||||
top: -6px;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid #fff;
|
||||
border-left: 6px solid transparent; }
|
||||
.daterangepicker.opensleft:before {
|
||||
right: 9px; }
|
||||
.daterangepicker.opensleft:after {
|
||||
right: 10px; }
|
||||
.daterangepicker.openscenter:before {
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto; }
|
||||
.daterangepicker.openscenter:after {
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto; }
|
||||
.daterangepicker.opensright:before {
|
||||
left: 9px; }
|
||||
.daterangepicker.opensright:after {
|
||||
left: 10px; }
|
||||
.daterangepicker.dropup {
|
||||
margin-top: -5px; }
|
||||
.daterangepicker.dropup:before {
|
||||
top: initial;
|
||||
bottom: -7px;
|
||||
border-bottom: initial;
|
||||
border-top: 7px solid #ccc; }
|
||||
.daterangepicker.dropup:after {
|
||||
top: initial;
|
||||
bottom: -6px;
|
||||
border-bottom: initial;
|
||||
border-top: 6px solid #fff; }
|
||||
.daterangepicker.dropdown-menu {
|
||||
max-width: none;
|
||||
z-index: 3001; }
|
||||
.daterangepicker.single .ranges, .daterangepicker.single .calendar {
|
||||
float: none; }
|
||||
.daterangepicker.show-calendar .calendar {
|
||||
display: block; }
|
||||
.daterangepicker .calendar {
|
||||
display: none;
|
||||
max-width: 270px;
|
||||
margin: 4px; }
|
||||
.daterangepicker .calendar.single .calendar-table {
|
||||
border: none; }
|
||||
.daterangepicker .calendar th, .daterangepicker .calendar td {
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
min-width: 32px; }
|
||||
.daterangepicker .calendar-table {
|
||||
border: 1px solid #fff;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
background: #fff; }
|
||||
.daterangepicker table {
|
||||
width: 100%;
|
||||
margin: 0; }
|
||||
.daterangepicker td, .daterangepicker th {
|
||||
text-align: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid transparent;
|
||||
white-space: nowrap;
|
||||
cursor: pointer; }
|
||||
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
|
||||
background: #eee; }
|
||||
.daterangepicker td.week, .daterangepicker th.week {
|
||||
font-size: 80%;
|
||||
color: #ccc; }
|
||||
.daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date {
|
||||
background-color: #fff;
|
||||
border-color: transparent;
|
||||
color: #999; }
|
||||
.daterangepicker td.in-range {
|
||||
background-color: #ebf4f8;
|
||||
border-color: transparent;
|
||||
color: #000;
|
||||
border-radius: 0; }
|
||||
.daterangepicker td.start-date {
|
||||
border-radius: 4px 0 0 4px; }
|
||||
.daterangepicker td.end-date {
|
||||
border-radius: 0 4px 4px 0; }
|
||||
.daterangepicker td.start-date.end-date {
|
||||
border-radius: 4px; }
|
||||
.daterangepicker td.active, .daterangepicker td.active:hover {
|
||||
background-color: #357ebd;
|
||||
border-color: transparent;
|
||||
color: #fff; }
|
||||
.daterangepicker th.month {
|
||||
width: auto; }
|
||||
.daterangepicker td.disabled, .daterangepicker option.disabled {
|
||||
color: #999;
|
||||
cursor: not-allowed;
|
||||
text-decoration: line-through; }
|
||||
.daterangepicker select.monthselect, .daterangepicker select.yearselect {
|
||||
font-size: 12px;
|
||||
padding: 1px;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
cursor: default; }
|
||||
.daterangepicker select.monthselect {
|
||||
margin-right: 2%;
|
||||
width: 56%; }
|
||||
.daterangepicker select.yearselect {
|
||||
width: 40%; }
|
||||
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect {
|
||||
width: 50px;
|
||||
margin-bottom: 0; }
|
||||
.daterangepicker .input-mini {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
color: #555;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
margin: 0 0 5px 0;
|
||||
padding: 0 6px 0 28px;
|
||||
width: 100%; }
|
||||
.daterangepicker .input-mini.active {
|
||||
border: 1px solid #08c;
|
||||
border-radius: 4px; }
|
||||
.daterangepicker .daterangepicker_input {
|
||||
position: relative; }
|
||||
.daterangepicker .daterangepicker_input i {
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 8px; }
|
||||
.daterangepicker .calendar-time {
|
||||
text-align: center;
|
||||
margin: 5px auto;
|
||||
line-height: 30px;
|
||||
position: relative;
|
||||
padding-left: 28px; }
|
||||
.daterangepicker .calendar-time select.disabled {
|
||||
color: #ccc;
|
||||
cursor: not-allowed; }
|
||||
|
||||
.ranges {
|
||||
font-size: 11px;
|
||||
float: none;
|
||||
margin: 4px;
|
||||
text-align: left; }
|
||||
.ranges ul {
|
||||
list-style: none;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
width: 100%; }
|
||||
.ranges li {
|
||||
font-size: 13px;
|
||||
background: #f5f5f5;
|
||||
border: 1px solid #f5f5f5;
|
||||
border-radius: 4px;
|
||||
color: #08c;
|
||||
padding: 3px 12px;
|
||||
margin-bottom: 8px;
|
||||
cursor: pointer; }
|
||||
.ranges li:hover {
|
||||
background: #08c;
|
||||
border: 1px solid #08c;
|
||||
color: #fff; }
|
||||
.ranges li.active {
|
||||
background: #08c;
|
||||
border: 1px solid #08c;
|
||||
color: #fff; }
|
||||
|
||||
/* Larger Screen Styling */
|
||||
@media (min-width: 564px) {
|
||||
.daterangepicker {
|
||||
width: auto; }
|
||||
.daterangepicker .ranges ul {
|
||||
width: 160px; }
|
||||
.daterangepicker.single .ranges ul {
|
||||
width: 100%; }
|
||||
.daterangepicker.single .calendar.left {
|
||||
clear: none; }
|
||||
.daterangepicker.single .ranges, .daterangepicker.single .calendar {
|
||||
float: left; }
|
||||
.daterangepicker .calendar.left {
|
||||
clear: left;
|
||||
margin-right: 0; }
|
||||
.daterangepicker .calendar.left .calendar-table {
|
||||
border-right: none;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0; }
|
||||
.daterangepicker .calendar.right {
|
||||
margin-left: 0; }
|
||||
.daterangepicker .calendar.right .calendar-table {
|
||||
border-left: none;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0; }
|
||||
.daterangepicker .left .daterangepicker_input {
|
||||
padding-right: 12px; }
|
||||
.daterangepicker .calendar.left .calendar-table {
|
||||
padding-right: 12px; }
|
||||
.daterangepicker .ranges, .daterangepicker .calendar {
|
||||
float: left; } }
|
||||
|
||||
@media (min-width: 730px) {
|
||||
.daterangepicker .ranges {
|
||||
width: auto;
|
||||
float: left; }
|
||||
.daterangepicker .calendar.left {
|
||||
clear: none; } }
|
||||
|
||||
Reference in New Issue
Block a user