Merge pull request #3 from pi-hole/devel

sync my repo
This commit is contained in:
TheME
2017-07-10 21:33:15 +02:00
committed by GitHub
12 changed files with 845 additions and 280 deletions
+8 -1
View File
@@ -164,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)
+47
View File
@@ -159,6 +159,53 @@ if (isset($_GET['getDBfilesize']) && $auth)
$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);
+67
View File
@@ -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>
-3
View File
@@ -34,9 +34,6 @@ $token = $_SESSION['token'];
<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>
-3
View File
@@ -34,9 +34,6 @@ $token = $_SESSION['token'];
<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>
+37 -2
View File
@@ -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">
+262
View File
@@ -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 = {
"&": "&amp;",
"<": "&lt;",
">": "&gt;",
"\"": "&quot;",
"\'": "&#039;"
};
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;
});
+8 -17
View File
@@ -15,28 +15,20 @@ 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" },
locale: { format: "MMMM Do YYYY, HH:mm" },
ranges: {
"Today": [moment(), moment()],
"Yesterday": [moment().subtract(1, "days"), moment().subtract(1, "days")],
"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().endOf("month")],
"Last Month": [moment().subtract(1, "month").startOf("month"), moment().subtract(1, "month").endOf("month")]
"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()]
},
startDate: start__, endDate: end__,
"opens": "center", "showDropdowns": true
},
function (startt, endt) {
@@ -182,8 +174,7 @@ function updateTopAdsChart() {
});
}
// Handle "Go" Button
$("#btnGo").on("click", function() {
$("#querytime").on("apply.daterangepicker", function(ev, picker) {
updateTopClientsChart();
updateTopDomainsChart();
updateTopAdsChart();
+43 -20
View File
@@ -13,30 +13,37 @@ 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 () {
// 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(
daterange = $("#querytime").daterangepicker(
{
timePicker: true, timePickerIncrement: 15,
locale: { format: "MMMM Do YYYY, h:mm A" },
locale: { format: "MMMM Do YYYY, HH:mm" },
ranges: {
"Today": [moment(), moment()],
"Yesterday": [moment().subtract(1, "days"), moment().subtract(1, "days")],
"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().endOf("month")],
"Last Month": [moment().subtract(1, "month").startOf("month"), moment().subtract(1, "month").endOf("month")]
"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()]
},
startDate: start__, endDate: end__,
"opens": "center", "showDropdowns": true
},
function (startt, endt) {
@@ -172,6 +179,16 @@ function refreshTableData() {
$(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)
@@ -213,7 +230,7 @@ $(document).ready(function() {
"<'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 },
"ajax": {"url": APIstring, "error": handleAjaxError },
"autoWidth" : false,
"processing": true,
"deferRender": true,
@@ -231,7 +248,8 @@ $(document).ready(function() {
"targets": -1,
"data": null,
"defaultContent": ""
} ]
} ],
"initComplete": reloadCallback
});
$("#all-queries tbody").on( "click", "button", function () {
var data = tableApi.row( $(this).parents("tr") ).data();
@@ -244,9 +262,14 @@ $(document).ready(function() {
add(data[2],"black");
}
} );
if(instantquery)
{
daterange.val(start__.format("MMMM Do YYYY, HH:mm") + " - " + end__.format("MMMM Do YYYY, HH:mm"));
}
} );
// Handle "Go" Button
$("#btnGo").on("click", function() {
$("#querytime").on("apply.daterangepicker", function(ev, picker) {
refreshTableData();
});
+364 -230
View File
@@ -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 = {
@@ -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();
}
});
+1 -1
View File
@@ -8,7 +8,7 @@
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
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
}
+8 -3
View File
@@ -235,7 +235,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" />
@@ -417,7 +417,7 @@ 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 } ?>">
<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">
@@ -425,6 +425,11 @@ if($auth) {
</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>
@@ -491,7 +496,7 @@ if($auth) {
<a href="#"><i class="fa fa-play"></i> <span id="enableLabel">Enable</span>&nbsp;&nbsp;&nbsp;<span id="flip-status-enable"></span></a>
</li>
<!-- Tools -->
<li class="treeview <?php if($scriptname === "gravity.php" || $scriptname === "queryads.php" || $scriptname === "debug.php" || $scriptname === "auditlog.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">