Long-term statistics implementation (using FTL's database) (#521)

* - Add DB Top Lists
- Add DB Query Log
- Add DB API

* Allow to select only queries that are equal/newer than the oldest entry in the database
This commit is contained in:
DL6ER
2017-06-29 20:57:09 +02:00
committed by GitHub
parent 99ffef6c8e
commit 9f7fb18f31
8 changed files with 2699 additions and 0 deletions
+169
View File
@@ -0,0 +1,169 @@
<?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["jsonForceObject"]))
{
echo json_encode($data, JSON_FORCE_OBJECT);
}
else
{
echo json_encode($data);
}
+145
View File
@@ -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>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">
<span class="input-group-btn">
<button id="btnGo" class="btn btn-default" type="button">Go!</button>
</span>
</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>
+148
View File
@@ -0,0 +1,148 @@
<?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">
<span class="input-group-btn">
<button id="btnGo" class="btn btn-default" type="button">Go!</button>
</span>
</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>
+190
View File
@@ -0,0 +1,190 @@
/* 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 () {
// Get first time stamp we have valid data for to limit selectable date/time range
$.getJSON("api_db.php?getMinTimestamp", function(data) {
var minDate = parseInt(data.mintimestamp);
if(!isNaN(minDate))
{
$("#querytime").data("daterangepicker").minDate = moment.unix(minDate);
}
});
$("#querytime").daterangepicker(
{
timePicker: true, timePickerIncrement: 15,
locale: { format: "MMMM Do YYYY, h:mm A" },
ranges: {
"Today": [moment(), moment()],
"Yesterday": [moment().subtract(1, "days"), moment().subtract(1, "days")],
"Last 7 Days": [moment().subtract(6, "days"), moment()],
"Last 30 Days": [moment().subtract(29, "days"), moment()],
"This Month": [moment().startOf("month"), moment().endOf("month")],
"Last Month": [moment().subtract(1, "month").startOf("month"), moment().subtract(1, "month").endOf("month")]
},
startDate: start__, endDate: end__,
"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 = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
"\"": "&quot;",
"\'": "&#039;"
};
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();
});
}
// Handle "Go" Button
$("#btnGo").on("click", function() {
updateTopClientsChart();
updateTopDomainsChart();
updateTopAdsChart();
});
+252
View File
@@ -0,0 +1,252 @@
/* 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 () {
// Get first time stamp we have valid data for to limit selectable date/time range
$.getJSON("api_db.php?getMinTimestamp", function(data) {
var minDate = parseInt(data.mintimestamp);
if(!isNaN(minDate))
{
$("#querytime").data("daterangepicker").minDate = moment.unix(minDate);
}
});
$("#querytime").daterangepicker(
{
timePicker: true, timePickerIncrement: 15,
locale: { format: "MMMM Do YYYY, h:mm A" },
ranges: {
"Today": [moment(), moment()],
"Yesterday": [moment().subtract(1, "days"), moment().subtract(1, "days")],
"Last 7 Days": [moment().subtract(6, "days"), moment()],
"Last 30 Days": [moment().subtract(29, "days"), moment()],
"This Month": [moment().startOf("month"), moment().endOf("month")],
"Last Month": [moment().subtract(1, "month").startOf("month"), moment().subtract(1, "month").endOf("month")]
},
startDate: start__, endDate: end__,
"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;
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": "api_db.php?getAllQueries=empty", "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": ""
} ]
});
$("#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");
}
} );
} );
// Handle "Go" Button
$("#btnGo").on("click", function() {
refreshTableData();
});
+21
View File
@@ -184,6 +184,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" />
@@ -416,6 +417,26 @@ 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"){ ?> 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_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">
+1542
View File
File diff suppressed because it is too large Load Diff
+232
View File
@@ -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; } }