mirror of
https://github.com/pi-hole/web.git
synced 2026-02-15 07:25:39 +00:00
418 lines
12 KiB
JavaScript
418 lines
12 KiB
JavaScript
/* 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 utils:false, Chart:false, moment:false */
|
|
|
|
var start__ = moment().subtract(7, "days");
|
|
var from = Math.round(moment(start__).utc().valueOf() / 1000);
|
|
var end__ = moment();
|
|
var until = Math.round(moment(end__).utc().valueOf() / 1000);
|
|
var interval = 0;
|
|
|
|
var dateformat = "MMMM Do YYYY, HH:mm";
|
|
|
|
// get the database min timestamp
|
|
var mintimestamp;
|
|
$.getJSON("api_db.php?getMinTimestamp", function (ts) {
|
|
mintimestamp = ts.mintimestamp * 1000 || 0; // return the timestamp in milliseconds or zero (in case of NaN)
|
|
});
|
|
|
|
$(function () {
|
|
$("#querytime").daterangepicker(
|
|
{
|
|
timePicker: true,
|
|
timePickerIncrement: 15,
|
|
timePicker24Hour: true,
|
|
locale: { format: dateformat },
|
|
startDate: start__,
|
|
endDate: end__,
|
|
ranges: {
|
|
Today: [moment().startOf("day"), moment()],
|
|
Yesterday: [
|
|
moment().subtract(1, "days").startOf("day"),
|
|
moment().subtract(1, "days").endOf("day"),
|
|
],
|
|
"Last 7 Days": [moment().subtract(7, "days"), moment()],
|
|
"Last 30 Days": [moment().subtract(30, "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(mintimestamp), moment()],
|
|
},
|
|
opens: "center",
|
|
showDropdowns: true,
|
|
autoUpdateInput: false,
|
|
},
|
|
function (startt, endt) {
|
|
from = Math.round(moment(startt).utc().valueOf() / 1000);
|
|
until = Math.round(moment(endt).utc().valueOf() / 1000);
|
|
}
|
|
);
|
|
});
|
|
|
|
var timeLineChart;
|
|
|
|
function compareNumbers(a, b) {
|
|
return a - b;
|
|
}
|
|
|
|
function computeInterval(from, until) {
|
|
// Compute interval to obtain about 200 values
|
|
var num = 200;
|
|
// humanly understandable intervals (in seconds)
|
|
var intervals = [
|
|
10,
|
|
20,
|
|
30,
|
|
60,
|
|
120,
|
|
180,
|
|
300,
|
|
600,
|
|
900,
|
|
1200,
|
|
1800,
|
|
3600,
|
|
3600 * 2,
|
|
3600 * 3,
|
|
3600 * 4,
|
|
3600 * 6,
|
|
3600 * 8,
|
|
3600 * 12,
|
|
3600 * 24,
|
|
3600 * 24 * 7,
|
|
3600 * 24 * 30,
|
|
];
|
|
|
|
var duration = until - from;
|
|
if (duration / (num * intervals[0]) < 1) {
|
|
return intervals[0];
|
|
}
|
|
|
|
var preverr = Number.MAX_VALUE,
|
|
err;
|
|
for (var i = 0; i < intervals.length; i++) {
|
|
err = Math.abs(1 - duration / (num * intervals[i]));
|
|
// pick the interval with least deviation
|
|
// from selected duration
|
|
if (preverr < err) {
|
|
return intervals[i - 1];
|
|
}
|
|
|
|
preverr = err;
|
|
}
|
|
|
|
return intervals[intervals.length - 1];
|
|
}
|
|
|
|
function updateQueriesOverTime() {
|
|
var timeoutWarning = $("#timeoutWarning");
|
|
|
|
$("#queries-over-time .overlay").show();
|
|
timeoutWarning.show();
|
|
|
|
interval = computeInterval(from, until);
|
|
// Default displaying axis scaling
|
|
timeLineChart.options.scales.xAxes.time.unit = "hour";
|
|
|
|
var duration = until - from;
|
|
// Xaxis scaling based on selected daterange
|
|
if (duration > 4 * 365 * 24 * 60 * 60) {
|
|
// If the requested data is more than 4 years, set ticks interval to year
|
|
timeLineChart.options.scales.xAxes.time.unit = "year";
|
|
} else if (duration >= 366 * 24 * 60 * 60) {
|
|
// If the requested data is more than 1 year, set ticks interval to quarter
|
|
timeLineChart.options.scales.xAxes.time.unit = "quarter";
|
|
} else if (duration >= 92 * 24 * 60 * 60) {
|
|
// If the requested data is more than 3 months, set ticks interval to months
|
|
timeLineChart.options.scales.xAxes.time.unit = "month";
|
|
} else if (duration >= 31 * 24 * 60 * 60) {
|
|
// If the requested data is 1 month or more, set ticks interval to weeks
|
|
timeLineChart.options.scales.xAxes.time.unit = "week";
|
|
} else if (duration > 3 * 24 * 60 * 60) {
|
|
// If the requested data is more than 3 days (72 hours), set ticks interval to days
|
|
timeLineChart.options.scales.xAxes.time.unit = "day";
|
|
}
|
|
|
|
$.getJSON(
|
|
"api_db.php?getGraphData&from=" + from + "&until=" + until + "&interval=" + interval,
|
|
function (data) {
|
|
// convert received objects to arrays
|
|
data.domains_over_time = utils.objectToArray(data.domains_over_time);
|
|
data.ads_over_time = utils.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 (Object.prototype.hasOwnProperty.call(data.domains_over_time[0], hour)) {
|
|
dates.push(parseInt(data.domains_over_time[0][hour], 10));
|
|
}
|
|
}
|
|
|
|
for (hour in data.ads_over_time[0]) {
|
|
if (
|
|
Object.prototype.hasOwnProperty.call(data.ads_over_time[0], hour) &&
|
|
dates.indexOf(parseInt(data.ads_over_time[0][hour], 10)) === -1
|
|
) {
|
|
dates.push(parseInt(data.ads_over_time[0][hour], 10));
|
|
}
|
|
}
|
|
|
|
dates.sort(compareNumbers);
|
|
|
|
// Add data for each hour that is available
|
|
for (hour in dates) {
|
|
if (Object.prototype.hasOwnProperty.call(dates, hour)) {
|
|
var date,
|
|
total = 0,
|
|
blocked = 0;
|
|
date = new Date(1000 * dates[hour]);
|
|
|
|
var idx = data.domains_over_time[0].indexOf(dates[hour].toString());
|
|
if (idx > -1) {
|
|
total = data.domains_over_time[1][idx];
|
|
}
|
|
|
|
idx = data.ads_over_time[0].indexOf(dates[hour].toString());
|
|
if (idx > -1) {
|
|
blocked = data.ads_over_time[1][idx];
|
|
}
|
|
|
|
timeLineChart.data.labels.push(date);
|
|
timeLineChart.data.datasets[0].data.push(blocked);
|
|
timeLineChart.data.datasets[1].data.push(total - blocked);
|
|
}
|
|
}
|
|
|
|
timeLineChart.options.scales.xAxes.ticks.min = from * 1000;
|
|
timeLineChart.options.scales.xAxes.ticks.max = until * 1000;
|
|
timeLineChart.options.scales.xAxes.display = true;
|
|
$("#queries-over-time .overlay").hide();
|
|
timeoutWarning.hide();
|
|
timeLineChart.update();
|
|
}
|
|
);
|
|
}
|
|
|
|
$(function () {
|
|
var ctx = document.getElementById("queryOverTimeChart").getContext("2d");
|
|
var blockedColor = $(".queries-blocked").css("background-color");
|
|
var permittedColor = $(".queries-permitted").css("background-color");
|
|
var gridColor = $(".graphs-grid").css("background-color");
|
|
var ticksColor = $(".graphs-ticks").css("color");
|
|
|
|
timeLineChart = new Chart(ctx, {
|
|
type: utils.getGraphType(),
|
|
data: {
|
|
labels: [],
|
|
datasets: [
|
|
{
|
|
label: "Blocked DNS Queries",
|
|
backgroundColor: blockedColor,
|
|
borderColor: blockedColor,
|
|
pointBorderColor: blockedColor,
|
|
data: [],
|
|
},
|
|
{
|
|
label: "Permitted DNS Queries",
|
|
backgroundColor: permittedColor,
|
|
borderColor: permittedColor,
|
|
pointBorderColor: permittedColor,
|
|
data: [],
|
|
},
|
|
],
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
interaction: {
|
|
mode: "nearest",
|
|
axis: "x",
|
|
},
|
|
plugins: {
|
|
tooltip: {
|
|
enabled: true,
|
|
yAlign: "bottom",
|
|
intersect: false,
|
|
itemSort: function (a, b) {
|
|
return b.datasetIndex - a.datasetIndex;
|
|
},
|
|
callbacks: {
|
|
label: function (tooltipLabel) {
|
|
var label = tooltipLabel.dataset.label;
|
|
// Add percentage only for blocked queries
|
|
if (tooltipLabel.datasetIndex === 0) {
|
|
var percentage = 0;
|
|
var permitted = parseInt(tooltipLabel.parsed._stacks.y[1], 10);
|
|
var blocked = parseInt(tooltipLabel.parsed._stacks.y[0], 10);
|
|
if (permitted + blocked > 0) {
|
|
percentage = (100 * blocked) / (permitted + blocked);
|
|
}
|
|
|
|
label += ": " + tooltipLabel.parsed.y + " (" + percentage.toFixed(1) + "%)";
|
|
} else {
|
|
label += ": " + tooltipLabel.parsed.y;
|
|
}
|
|
|
|
return label;
|
|
},
|
|
title: function (tooltipTitle) {
|
|
var title = tooltipTitle[0].label;
|
|
var time = new Date(title);
|
|
var fromDate =
|
|
time.getFullYear() +
|
|
"-" +
|
|
utils.padNumber(time.getMonth() + 1) +
|
|
"-" +
|
|
utils.padNumber(time.getDate());
|
|
var fromTime =
|
|
utils.padNumber(time.getHours()) +
|
|
":" +
|
|
utils.padNumber(time.getMinutes()) +
|
|
":" +
|
|
utils.padNumber(time.getSeconds());
|
|
time = new Date(time.valueOf() + 1000 * interval);
|
|
var untilDate =
|
|
time.getFullYear() +
|
|
"-" +
|
|
utils.padNumber(time.getMonth() + 1) +
|
|
"-" +
|
|
utils.padNumber(time.getDate());
|
|
var untilTime =
|
|
utils.padNumber(time.getHours()) +
|
|
":" +
|
|
utils.padNumber(time.getMinutes()) +
|
|
":" +
|
|
utils.padNumber(time.getSeconds());
|
|
|
|
if (fromDate === untilDate) {
|
|
// Abbreviated form for intervals on the same day
|
|
// We split title in two lines on small screens
|
|
if ($(window).width() < 992) {
|
|
untilTime += "\n";
|
|
}
|
|
|
|
return ("Queries from " + fromTime + " to " + untilTime + " on " + fromDate).split(
|
|
"\n "
|
|
);
|
|
}
|
|
|
|
// Full tooltip for intervals spanning more than one day
|
|
// We split title in two lines on small screens
|
|
if ($(window).width() < 992) {
|
|
fromDate += "\n";
|
|
}
|
|
|
|
return (
|
|
"Queries from " +
|
|
fromDate +
|
|
" " +
|
|
fromTime +
|
|
" to " +
|
|
untilDate +
|
|
" " +
|
|
untilTime
|
|
).split("\n ");
|
|
},
|
|
},
|
|
},
|
|
legend: {
|
|
display: false,
|
|
},
|
|
},
|
|
scales: {
|
|
xAxes: {
|
|
type: "time",
|
|
stacked: true,
|
|
offset: false,
|
|
time: {
|
|
unit: "hour",
|
|
displayFormats: {
|
|
minute: "HH:mm",
|
|
hour: "HH:mm",
|
|
day: "MMM DD",
|
|
week: "MMM DD",
|
|
month: "MMM",
|
|
quarter: "YYYY MMM",
|
|
year: "YYYY",
|
|
},
|
|
},
|
|
grid: {
|
|
color: gridColor,
|
|
drawBorder: false,
|
|
offset: false,
|
|
},
|
|
ticks: {
|
|
color: ticksColor,
|
|
},
|
|
},
|
|
yAxes: {
|
|
stacked: true,
|
|
beginAtZero: true,
|
|
ticks: {
|
|
color: ticksColor,
|
|
precision: 0,
|
|
},
|
|
grid: {
|
|
color: gridColor,
|
|
drawBorder: false,
|
|
},
|
|
},
|
|
},
|
|
elements: {
|
|
line: {
|
|
borderWidth: 0,
|
|
spanGaps: false,
|
|
fill: true,
|
|
},
|
|
point: {
|
|
radius: 0,
|
|
hoverRadius: 5,
|
|
hitRadius: 5,
|
|
},
|
|
},
|
|
maintainAspectRatio: false,
|
|
},
|
|
});
|
|
});
|
|
|
|
$("#querytime").on("apply.daterangepicker", function (ev, picker) {
|
|
$(this).val(picker.startDate.format(dateformat) + " to " + picker.endDate.format(dateformat));
|
|
$("#queries-over-time").show();
|
|
updateQueriesOverTime();
|
|
});
|
|
|
|
$("#queryOverTimeChart").on("click", function (evt) {
|
|
var activePoints = timeLineChart.getElementsAtEventForMode(
|
|
evt,
|
|
"nearest",
|
|
{ intersect: true },
|
|
false
|
|
);
|
|
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 + interval;
|
|
window.location.href = "db_queries.php?from=" + from + "&until=" + until;
|
|
}
|
|
|
|
return false;
|
|
});
|