Add live-tail of all log files as well as local DNS and CNAME records handling (add new and delete existing records is still TODO at this point)

Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
DL6ER
2023-02-20 15:41:12 +01:00
parent 90aa9c9ac1
commit 5f7ceda54f
13 changed files with 564 additions and 422 deletions

View File

@@ -1,139 +0,0 @@
/* 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 */
var table;
var token = $("#token").text();
$(function () {
$("#btnAdd").on("click", addCustomCNAME);
table = $("#customCNAMETable").DataTable({
ajax: {
url: "scripts/pi-hole/php/customcname.php",
data: { action: "get", token: token },
type: "POST",
},
columns: [{}, {}, { orderable: false, searchable: false }],
columnDefs: [
{
targets: 2,
render: function (data, type, row) {
return (
'<button type="button" class="btn btn-danger btn-xs deleteCustomCNAME" data-domain=\'' +
row[0] +
"' data-target='" +
row[1] +
"'>" +
'<span class="far fa-trash-alt"></span>' +
"</button>"
);
},
},
{
targets: "_all",
render: $.fn.dataTable.render.text(),
},
],
lengthMenu: [
[10, 25, 50, 100, -1],
[10, 25, 50, 100, "All"],
],
order: [[0, "asc"]],
stateSave: true,
stateDuration: 0,
stateSaveCallback: function (settings, data) {
utils.stateSaveCallback("LocalCNAMETable", data);
},
stateLoadCallback: function () {
return utils.stateLoadCallback("LocalCNAMETable");
},
drawCallback: function () {
$(".deleteCustomCNAME").on("click", deleteCustomCNAME);
},
});
// Disable autocorrect in the search box
var input = document.querySelector("input[type=search]");
input.setAttribute("autocomplete", "off");
input.setAttribute("autocorrect", "off");
input.setAttribute("autocapitalize", "off");
input.setAttribute("spellcheck", false);
});
function addCustomCNAME() {
var domain = utils.escapeHtml($("#domain").val());
var target = utils.escapeHtml($("#target").val());
utils.disableAll();
utils.showAlert("info", "", "Adding custom CNAME record...", "");
$.ajax({
url: "scripts/pi-hole/php/customcname.php",
method: "post",
dataType: "json",
data: { action: "add", domain: domain, target: target, token: token },
success: function (response) {
utils.enableAll();
if (response.success) {
utils.showAlert(
"success",
"far fa-check-circle",
"Custom CNAME added",
domain + ": " + target
);
// Clean up field values and reload table data
$("#domain").val("");
$("#target").val("");
table.ajax.reload();
$("#domain").focus();
} else {
utils.showAlert("error", "fas fa-times", "Failure! Something went wrong", response.message);
}
},
error: function () {
utils.enableAll();
utils.showAlert("error", "fas fa-times", "Error while adding custom CNAME record", "");
},
});
}
function deleteCustomCNAME() {
var domain = $(this).attr("data-domain");
var target = $(this).attr("data-target");
utils.disableAll();
utils.showAlert("info", "", "Deleting custom CNAME record...", "");
$.ajax({
url: "scripts/pi-hole/php/customcname.php",
method: "post",
dataType: "json",
data: { action: "delete", domain: domain, target: target, token: token },
success: function (response) {
utils.enableAll();
if (response.success) {
utils.showAlert(
"success",
"far fa-check-circle",
"Custom CNAME deleted",
domain + ": " + target
);
table.ajax.reload();
} else {
utils.showAlert("error", "fas fa-times", "Failure! Something went wrong", response.message);
}
},
error: function (jqXHR, exception) {
utils.enableAll();
utils.showAlert("error", "fas fa-times", "Error while deleting custom CNAME record", "");
console.log(exception); // eslint-disable-line no-console
},
});
}

View File

@@ -1,128 +0,0 @@
/* 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 */
var table;
var token = $("#token").text();
$(function () {
$("#btnAdd").on("click", addCustomDNS);
table = $("#customDNSTable").DataTable({
ajax: {
url: "scripts/pi-hole/php/customdns.php",
data: { action: "get", token: token },
type: "POST",
},
columns: [{}, { type: "ip-address" }, { orderable: false, searchable: false }],
columnDefs: [
{
targets: 2,
render: function (data, type, row) {
return (
'<button type="button" class="btn btn-danger btn-xs deleteCustomDNS" data-domain=\'' +
row[0] +
"' data-ip='" +
row[1] +
"'>" +
'<span class="far fa-trash-alt"></span>' +
"</button>"
);
},
},
{
targets: "_all",
render: $.fn.dataTable.render.text(),
},
],
lengthMenu: [
[10, 25, 50, 100, -1],
[10, 25, 50, 100, "All"],
],
order: [[0, "asc"]],
stateSave: true,
stateDuration: 0,
stateSaveCallback: function (settings, data) {
utils.stateSaveCallback("LocalDNSTable", data);
},
stateLoadCallback: function () {
return utils.stateLoadCallback("LocalDNSTable");
},
drawCallback: function () {
$(".deleteCustomDNS").on("click", deleteCustomDNS);
},
});
// Disable autocorrect in the search box
var input = document.querySelector("input[type=search]");
input.setAttribute("autocomplete", "off");
input.setAttribute("autocorrect", "off");
input.setAttribute("autocapitalize", "off");
input.setAttribute("spellcheck", false);
});
function addCustomDNS() {
var ip = utils.escapeHtml($("#ip").val());
var domain = utils.escapeHtml($("#domain").val());
utils.disableAll();
utils.showAlert("info", "", "Adding custom DNS entry...", "");
$.ajax({
url: "scripts/pi-hole/php/customdns.php",
method: "post",
dataType: "json",
data: { action: "add", ip: ip, domain: domain, token: token },
success: function (response) {
utils.enableAll();
if (response.success) {
utils.showAlert("success", "far fa-check-circle", "Custom DNS added", domain + ": " + ip);
// Clean up field values and reload table data
$("#domain").val("");
$("#ip").val("");
table.ajax.reload();
$("#domain").focus();
} else {
utils.showAlert("error", "fas fa-times", "Failure! Something went wrong", response.message);
}
},
error: function () {
utils.enableAll();
utils.showAlert("error", "fas fa-times", "Error while adding custom DNS entry", "");
},
});
}
function deleteCustomDNS() {
var ip = $(this).attr("data-ip");
var domain = $(this).attr("data-domain");
utils.disableAll();
utils.showAlert("info", "", "Deleting custom DNS entry...", "");
$.ajax({
url: "scripts/pi-hole/php/customdns.php",
method: "post",
dataType: "json",
data: { action: "delete", domain: domain, ip: ip, token: token },
success: function (response) {
utils.enableAll();
if (response.success) {
utils.showAlert("success", "far fa-check-circle", "Custom DNS deleted", domain + ": " + ip);
table.ajax.reload();
} else {
utils.showAlert("error", "fas fa-times", "Failure! Something went wrong", response.message);
}
},
error: function (jqXHR, exception) {
utils.enableAll();
utils.showAlert("error", "fas fa-times", "Error while deleting custom DNS entry", "");
console.log(exception); // eslint-disable-line no-console
},
});
}

View File

@@ -136,7 +136,7 @@ $(function () {
[10, 25, 50, 100, "All"],
],
language: {
emptyTable: "No issues found.",
emptyTable: "No DHCP leases found.",
},
stateSave: true,
stateDuration: 0,

View File

@@ -0,0 +1,244 @@
/* Pi-hole: A black hole for Internet advertisements
* (c) 2023 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.
* Precord see LICENSE file for your rights under this license. */
/* global utils: false */
var dnsRecordsTable = null;
var customCNAMETable = null;
$(function () {
dnsRecordsTable = $("#dnsRecordsTable").DataTable({
ajax: {
url: "/api/config/dns/hosts",
type: "GET",
dataSrc: "config.dns.hosts",
},
order: [[0, "asc"]],
columns: [
{ data: null },
{ data: null, type: "ip-address" },
{ data: null, width: "22px", orderable: false },
],
columnDefs: [
{
targets: "_all",
render: $.fn.dataTable.render.text(),
},
],
drawCallback: function () {
$('button[id^="deleteRecord"]').on("click", deleteRecord);
// Remove visible dropdown to prevent orphaning
$("body > .bootstrap-select.dropdown").remove();
},
rowCallback: function (row, data) {
$(row).attr("data-id", data.ip);
var button =
'<button type="button" class="btn btn-danger btn-xs" id="deleteRecord' +
data.ip +
'" data-del-ip="' +
data.ip +
'">' +
'<span class="far fa-trash-alt"></span>' +
"</button>";
$("td:eq(2)", row).html(button);
// Split record in format IP NAME1 [NAME2 [NAME3 [NAME...]]]
var ip = data.substring(0, data.indexOf(" "));
// The name can be mulitple domains separated by spaces
var name = data.substring(data.indexOf(" ") + 1);
$("td:eq(0)", row).text(name);
$("td:eq(1)", row).text(ip);
},
dom:
"<'row'<'col-sm-6'l><'col-sm-6'f>>" +
"<'row'<'col-sm-12'<'table-responsive'tr>>>" +
"<'row'<'col-sm-12'i>>",
lengthMenu: [
[10, 25, 50, 100, -1],
[10, 25, 50, 100, "All"],
],
language: {
emptyTable: "No local DNS records defined.",
},
stateSave: true,
stateDuration: 0,
stateSaveCallback: function (settings, data) {
utils.stateSaveCallback("dns-records-table", data);
},
stateLoadCallback: function () {
var data = utils.stateLoadCallback("dns-records-table");
// Return if not available
if (data === null) {
return null;
}
// Apply loaded state to table
return data;
},
});
dnsRecordsTable = $("#customCNAMETable").DataTable({
ajax: {
url: "/api/config/dns/cnameRecords",
type: "GET",
dataSrc: "config.dns.cnameRecords",
},
order: [[0, "asc"]],
columns: [
{ data: null },
{ data: null },
{ data: null },
{ data: null, width: "22px", orderable: false },
],
columnDefs: [
{
targets: "_all",
render: $.fn.dataTable.render.text(),
},
],
drawCallback: function () {
$('button[id^="deleteCNAME"]').on("click", delCNAMERecord);
// Remove visible dropdown to prevent orphaning
$("body > .bootstrap-select.dropdown").remove();
},
rowCallback: function (row, data) {
$(row).attr("data-id", data.ip);
var button =
'<button type="button" class="btn btn-danger btn-xs" id="deleteCNAME' +
data.ip +
'" data-del-ip="' +
data.ip +
'">' +
'<span class="far fa-trash-alt"></span>' +
"</button>";
$("td:eq(3)", row).html(button);
// Split record in format <cname>,<target>[,<TTL>]
var splitted = data.split(",");
$("td:eq(0)", row).text(splitted[0]);
$("td:eq(1)", row).text(splitted[1]);
if (splitted.length > 2) $("td:eq(2)", row).text(splitted[2]);
else $("td:eq(2)", row).text("-");
},
dom:
"<'row'<'col-sm-6'l><'col-sm-6'f>>" +
"<'row'<'col-sm-12'<'table-responsive'tr>>>" +
"<'row'<'col-sm-12'i>>",
lengthMenu: [
[10, 25, 50, 100, -1],
[10, 25, 50, 100, "All"],
],
language: {
emptyTable: "No local CNAME records defined.",
},
stateSave: true,
stateDuration: 0,
stateSaveCallback: function (settings, data) {
utils.stateSaveCallback("dns-cname-records-table", data);
},
stateLoadCallback: function () {
var data = utils.stateLoadCallback("dns-cname-records-table");
// Return if not available
if (data === null) {
return null;
}
// Apply loaded state to table
return data;
},
});
});
function deleteRecord() {
// Passes the button data-del-id attribute as IP
var ips = [$(this).attr("data-del-ip")];
// Check input validity
if (!Array.isArray(ips)) return;
// Exploit prevention: Return early for non-numeric IDs
for (var ip in ips) {
if (Object.hasOwnProperty.call(ips, ip)) {
delRecord(ips);
}
}
}
function delRecord(ip) {
utils.disableAll();
utils.showAlert("info", "", "Deleting DNS record...");
$.ajax({
url: "/api/dns/records/" + ip /* TODO */,
method: "DELETE",
})
.done(function (response) {
utils.enableAll();
if (response === undefined) {
utils.showAlert("success", "far fa-trash-alt", "Successfully deleted DNS record", "");
dnsRecordsTable.ajax.reload(null, false);
} else {
utils.showAlert("error", "", "Error while deleting DNS record: " + ip, response.record);
}
})
.done(
utils.checkrecords // Update icon warnings count
)
.fail(function (jqXHR, exception) {
utils.enableAll();
utils.showAlert("error", "", "Error while deleting DNS record: " + ip, jqXHR.responseText);
console.log(exception); // eslint-disable-line no-console
});
}
function delCNAMERecord(ip) {
utils.disableAll();
utils.showAlert("info", "", "Deleting local CNAME record...");
$.ajax({
url: "/api/CNAME/records/" + ip /* TODO */,
method: "DELETE",
})
.done(function (response) {
utils.enableAll();
if (response === undefined) {
utils.showAlert(
"success",
"far fa-trash-alt",
"Successfully deleted local CNAME record",
""
);
customCNAMETable.ajax.reload(null, false);
} else {
utils.showAlert(
"error",
"",
"Error while deleting local CNAME record: " + ip,
response.record
);
}
})
.done(
utils.checkrecords // Update icon warnings count
)
.fail(function (jqXHR, exception) {
utils.enableAll();
utils.showAlert(
"error",
"",
"Error while deleting local CNAME record: " + ip,
jqXHR.responseText
);
console.log(exception); // eslint-disable-line no-console
});
}
/*
$(document).ready(function () {
processDNSRecordsConfig();
});
*/

View File

@@ -6,7 +6,6 @@
* Please see LICENSE file for your rights under this license. */
/* global apiFailure:false */
/* exported updateHostInfo, updateCacheInfo */
var hostinfoTimer = null;

View File

@@ -1,49 +0,0 @@
/* 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. */
var offset,
timer,
pre,
scrolling = true;
// Check every 200msec for fresh data
var interval = 200;
// Function that asks the API for new data
function reloadData() {
clearTimeout(timer);
$.getJSON("scripts/pi-hole/php/tailLog.php?FTL&offset=" + offset, function (data) {
pre.append(data.lines);
if (scrolling && offset !== data.offset) {
pre.scrollTop(pre[0].scrollHeight);
}
offset = data.offset;
});
timer = setTimeout(reloadData, interval);
}
$(function () {
// Get offset at first loading of page
$.getJSON("scripts/pi-hole/php/tailLog.php?FTL", function (data) {
offset = data.offset;
});
pre = $("#output");
// Trigger function that looks for new data
reloadData();
});
$("#chk1").on("click", function () {
$("#chk2").prop("checked", this.checked);
scrolling = this.checked;
});
$("#chk2").on("click", function () {
$("#chk1").prop("checked", this.checked);
scrolling = this.checked;
});

View File

@@ -5,45 +5,124 @@
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
var offset,
timer,
pre,
scrolling = true;
/* global moment: false, apiFailure: false */
// Check every 200msec for fresh data
var interval = 200;
var nextID = 0;
// Check every 0.5s for fresh data
const interval = 500;
// Maximum number of lines to display
const maxlines = 5000;
// Fade in new lines
const fadeIn = true;
// Mark new lines with a red line above them
const markUpdates = true;
// Function that asks the API for new data
function reloadData() {
clearTimeout(timer);
$.getJSON("scripts/pi-hole/php/tailLog.php?offset=" + offset, function (data) {
pre.append(data.lines);
if (scrolling && offset !== data.offset) {
pre.scrollTop(pre[0].scrollHeight);
function getData() {
// Only update when spinner is spinning
if (!$("#feed-icon").hasClass("fa-play")) {
// Restart timer
setTimeout(getData, interval);
return;
}
offset = data.offset;
var GETDict = {};
window.location.search
.substr(1)
.split("&")
.forEach(function (item) {
GETDict[item.split("=")[0]] = item.split("=")[1];
});
if (!("file" in GETDict)) {
window.location.href += "?file=dnsmasq";
return;
}
$.ajax({
url: "/api/logs/" + GETDict.file + "?nextID=" + nextID,
method: "GET",
})
.done(function (data) {
if (data.log.length === 0) {
if (nextID === 0) {
$("#output").html("<i>*** Log file is empty ***</i>");
}
// Restart timer
setTimeout(getData, interval);
return;
}
// We have new lines
if (markUpdates && nextID > 0) {
// Add red fading out background to new lines
$("#output").append('<hr class="hr-small">').children(":last").fadeOut(2000);
}
data.log.forEach(function (line) {
// Add new line to output
$("#output").append(
'<span><span style="border-left: 2px red" class="left-line">&nbsp;</span><span class="text-muted">' +
moment(1000 * line.timestamp).format("YYYY-MM-DD HH:mm:ss.SSS") +
"</span> " +
line.message +
"<br></span>"
);
if (fadeIn) {
//$(".left-line:last").fadeOut(2000);
$("#output").children(":last").hide().fadeIn("fast");
}
});
timer = setTimeout(reloadData, interval);
// Limit output to <maxlines> lines
var lines = $("#output").val().split("\n");
if (lines.length > maxlines) {
lines.splice(0, lines.length - maxlines);
$("#output").val(lines.join("\n"));
}
// Scroll to bottom of output
$("#output").scrollTop($("#output")[0].scrollHeight);
// Update nextID
nextID = data.nextID;
// Set filename
$("#filename").text(data.file);
// Restart timer
setTimeout(getData, interval);
})
.fail(function (data) {
apiFailure(data);
});
}
$(function () {
// Get offset at first loading of page
$.getJSON("scripts/pi-hole/php/tailLog.php", function (data) {
offset = data.offset;
});
pre = $("#output");
// Trigger function that looks for new data
reloadData();
});
getData();
$("#chk1").on("click", function () {
$("#chk2").prop("checked", this.checked);
scrolling = this.checked;
// Clicking on the element with class "fa-spinner" will toggle the play/pause state
$("#live-feed").on("click", function () {
if ($("#feed-icon").hasClass("fa-play")) {
// Toggle button color
$("#feed-icon").addClass("fa-pause");
$("#feed-icon").removeClass("fa-fade");
$("#feed-icon").removeClass("fa-play");
$(this).addClass("btn-danger");
$(this).removeClass("btn-success");
$("#title").text("Paused");
} else {
// Toggle button color
$("#feed-icon").addClass("fa-play");
$("#feed-icon").addClass("fa-fade");
$("#feed-icon").removeClass("fa-pause");
$(this).addClass("btn-success");
$(this).removeClass("btn-danger");
$("#title").text("Live");
}
});
$("#chk2").on("click", function () {
$("#chk1").prop("checked", this.checked);
scrolling = this.checked;
});

View File

@@ -162,12 +162,7 @@
</li>
<li class="<?php if ($scriptname === 'settings-dns-records.php') { ?> active<?php } ?>">
<a href="settings-dns-records.php">
<i class="fa-fw menu-icon fa-solid fa-address-book"></i> DNS Records
</a>
</li>
<li class="<?php if ($scriptname === 'settings-cname-records.php') { ?> active<?php } ?>">
<a href="settings-cname-records.php">
<i class="fa-fw menu-icon fa-solid fa-address-book"></i> CNAME Records
<i class="fa-fw menu-icon fa-solid fa-address-book"></i> Local DNS Records
</a>
</li>
</ul>
@@ -189,6 +184,41 @@
<span class="pull-right-container warning-count hidden"></span>
</a>
</li>
<!-- Tail log files -->
<li class="treeview <?php if ($scriptname === 'taillog.php') { ?> active<?php } ?>">
<a href="#">
<i class="fa-fw menu-icon fa-solid fa-list-ul"></i> Tail log files
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu">
<!-- Tail pihole.log -->
<li class="<?php if ($scriptname === 'taillog.php' && $_GET["file"] == "dnsmasq") { ?> active<?php } ?>">
<a href="taillog.php?file=dnsmasq">
<i class="fa-fw menu-icon fa-solid fa-list-ul"></i> <code>pihole.log</code>
</a>
</li>
<!-- Tail FTL.log -->
<li class="<?php if ($scriptname === 'taillog.php' && $_GET["file"] == "ftl") { ?> active<?php } ?>">
<a href="taillog.php?file=ftl">
<i class="fa-fw menu-icon fa-solid fa-list-ul"></i> <code>FTL.log</code>
</a>
</li>
<!-- Tail civetweb.log -->
<li class="<?php if ($scriptname === 'taillog.php' && $_GET["file"] == "http") { ?> active<?php } ?>">
<a href="taillog.php?file=http">
<i class="fa-fw menu-icon fa-solid fa-list-ul"></i> <code>civetweb.log</code>
</a>
</li>
<!-- Tail ph7.log -->
<li class="<?php if ($scriptname === 'taillog.php' && $_GET["file"] == "ph7") { ?> active<?php } ?>">
<a href="taillog.php?file=ph7">
<i class="fa-fw menu-icon fa-solid fa-list-ul"></i> <code>ph7.log</code>
</a>
</li>
</ul>
</li>
<!-- Run gravity.sh -->
<li class="<?php if ($scriptname === 'gravity.php') { ?> active<?php } ?>">
<a href="gravity.php">
@@ -201,20 +231,6 @@
<i class="fa fa-fw menu-icon fa-search"></i> Search Adlists
</a>
</li>
<!-- Tail pihole.log -->
<li class="<?php if ($scriptname === 'taillog.php') { ?> active<?php } ?>">
<a href="taillog.php">
<svg class="svg-inline--fa fa-fw menu-icon" style="height: 1.25em"><use xlink:href="img/pihole_icon.svg#pihole-svg-logo"/></svg>
Tail pihole.log
</a>
</li>
<!-- Tail FTL.log -->
<li class="<?php if ($scriptname === 'taillog-FTL.php') { ?> active<?php } ?>">
<a href="taillog-FTL.php">
<svg class="svg-inline--fa fa-fw menu-icon" style="height: 1.25em"><use xlink:href="img/pihole_icon.svg#pihole-svg-logo"/></svg>
Tail FTL.log
</a>
</li>
<!-- Generate debug log -->
<li class="<?php if ($scriptname === 'debug.php') { ?> active<?php } ?>">
<a href="debug.php">

141
settings-dns-records.php Normal file
View File

@@ -0,0 +1,141 @@
<?php
/*
* Pi-hole: A black hole for Internet advertisements
* (c) 2023 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_authenticated.php';
?>
<div class="row">
<div class="col-md-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Local DNS records</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-md-12">
<div class="box collapsed-box">
<!-- /.box-header -->
<div class="box-header with-border">
<h3 class="box-title">
Add new DNS record
</h3>
<div class="box-tools pull-right">
<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-plus"></i></button>
</div>
</div>
<!-- /.box-header -->
<div class="box-body">
<div class="row">
<div class="form-group col-md-6">
<label for="domain">Domain:</label>
<input id="domain" type="url" class="form-control" placeholder="Add a domain (example.com or sub.example.com)" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
</div>
<div class="form-group col-md-6">
<label for="target">IP address:</label>
<input id="ip" type="text" class="form-control" placeholder="Associated IP address" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
</div>
</div>
</div>
<div class="box-footer clearfix">
<strong>Note:</strong>
<p>The target of a <code>CNAME</code> must be a domain that the Pi-hole already has in its cache or is authoritative for. This is a universal limitation of <code>CNAME</code> records.</p>
<p>The reason for this is that Pi-hole will not send additional queries upstream when serving <code>CNAME</code> replies. As consequence, if you set a target that isn't already known, the reply to the client may be incomplete. Pi-hole just returns the information it knows at the time of the query. This results in certain limitations for <code>CNAME</code> targets,
for instance, only <i>active</i> DHCP leases work as targets - mere DHCP <i>leases</i> aren't sufficient as they aren't (yet) valid DNS records.</p>
<p>Additionally, you can't <code>CNAME</code> external domains (<code>bing.com</code> to <code>google.com</code>) successfully as this could result in invalid SSL certificate errors when the target server does not serve content for the requested domain.</p>
<button type="button" id="btnAdd" class="btn btn-primary pull-right">Add</button>
</div>
</div>
</div>
<div class="col-md-12">
<h3 class="box-title">List of local DNS records</h3>
<!-- /.box-header -->
<table id="dnsRecordsTable" class="table table-striped table-bordered" width="100%">
<thead>
<tr>
<th>Domain</th>
<th>IP</th>
<th>Action</th>
</tr>
</thead>
</table>
<button type="button" id="resetButton" class="btn btn-default btn-sm text-red hidden">Clear Filters</button>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Local CNAME records records</h3>
</div>
<div class="box-body">
<div class="row">
<div class="col-md-12">
<div class="box collapsed-box">
<!-- /.box-header -->
<div class="box-header with-border">
<h3 class="box-title">
Add new CNAME record
</h3>
<div class="box-tools pull-right">
<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-plus"></i></button>
</div>
</div>
<!-- /.box-header -->
<div class="box-body">
<div class="row">
<div class="form-group col-md-6">
<label for="domain">Domain:</label>
<input id="domain" type="url" class="form-control" placeholder="Add a domain (example.com or sub.example.com)" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
</div>
<div class="form-group col-md-6">
<label for="target">Target Domain:</label>
<input id="target" type="url" class="form-control" placeholder="Associated Target Domain" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
</div>
</div>
</div>
<div class="box-footer clearfix">
<strong>Note:</strong>
<p>The target of a <code>CNAME</code> must be a domain that the Pi-hole already has in its cache or is authoritative for. This is a universal limitation of <code>CNAME</code> records.</p>
<p>The reason for this is that Pi-hole will not send additional queries upstream when serving <code>CNAME</code> replies. As consequence, if you set a target that isn't already known, the reply to the client may be incomplete. Pi-hole just returns the information it knows at the time of the query. This results in certain limitations for <code>CNAME</code> targets,
for instance, only <i>active</i> DHCP leases work as targets - mere DHCP <i>leases</i> aren't sufficient as they aren't (yet) valid DNS records.</p>
<p>Additionally, you can't <code>CNAME</code> external domains (<code>bing.com</code> to <code>google.com</code>) successfully as this could result in invalid SSL certificate errors when the target server does not serve content for the requested domain.</p>
<button type="button" id="btnAdd" class="btn btn-primary pull-right">Add</button>
</div>
</div>
</div>
<div class="col-md-12">
<h3 class="box-title">List of local CNAME records</h3>
<!-- /.box-header -->
<table id="customCNAMETable" class="table table-striped table-bordered" width="100%">
<thead>
<tr>
<th>Domain</th>
<th>Target</th>
<th>TTL</th>
<th>Action</th>
</tr>
</thead>
</table>
<button type="button" id="resetButton" class="btn btn-default btn-sm text-red hidden">Clear Filters</button>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="<?php echo fileversion('scripts/vendor/jquery.confirm.min.js'); ?>"></script>
<script src="<?php echo fileversion('scripts/pi-hole/js/settings-dns-records.js'); ?>"></script>
<script src="<?php echo fileversion('scripts/pi-hole/js/ip-address-sorting.js'); ?>"></script>
<script src="<?php echo fileversion('scripts/pi-hole/js/settings.js'); ?>"></script>
<?php
require 'scripts/pi-hole/php/footer.php';
?>

View File

@@ -1055,3 +1055,14 @@ table.dataTable tbody > tr > .selected {
.faint-border {
border-color: rgba(127, 127, 127, 0.1);
}
.pre-scrollable {
max-height: calc(100vh - 300px);
overflow-y: scroll;
}
.hr-small {
margin-top: 0px;
margin-bottom: 0px;
border-top: 1px solid #ff0000aa;
}

View File

@@ -190,10 +190,6 @@ h4 {
.box > .box-header {
color: #bec5cb;
}
.box-solid > .box-header .btn,
.box > .box-header .btn {
color: #bec5cb;
}
.box.box-info,
.box.box-primary,
.box.box-success,

View File

@@ -1,34 +0,0 @@
<?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_authenticated.php';
?>
<!-- Title -->
<div class="page-header">
<h1>Output the last lines of the FTL.log file (live)</h1>
</div>
<div>
<input type="checkbox" checked id="chk1">
<label for="chk1">Automatic scrolling on update</label>
</div>
<pre id="output" style="width: 100%; height: 100%; max-height:650px; overflow-y:scroll;"></pre>
<div>
<input type="checkbox" checked id="chk2">
<label for="chk2">Automatic scrolling on update</label>
</div>
<script src="<?php echo fileversion('scripts/pi-hole/js/taillog-FTL.js'); ?>"></script>
<?php
require 'scripts/pi-hole/php/footer.php';
?>

View File

@@ -10,21 +10,27 @@
require 'scripts/pi-hole/php/header_authenticated.php';
?>
<!-- Title -->
<div class="page-header">
<h1>Output the last lines of the pihole.log file (live)</h1>
<div class="row">
<div class="col-md-12">
<div class="box box-warning">
<div class="box-header with-border">
<h3 class="box-title"><code>tail -F <span id="filename">...</span></code></h3>
<div class="pull-right">
<button class="btn btn-success" id="live-feed">
<span id="title">Live</span>&nbsp;&nbsp;
<i id="feed-icon" class="fa-solid fa-fw fa-play fa-fade"></i>
</button>
</div>
</div>
<div class="box-body">
<div class="row">
<div class="col-md-12">
<pre id="output" class="pre pre-scrollable"></pre>
</div>
</div>
</div>
</div>
<div>
<input type="checkbox" checked id="chk1">
<label for="chk1">Automatic scrolling on update</label>
</div>
<pre id="output" style="width: 100%; height: 100%; max-height:650px; overflow-y:scroll;"></pre>
<div>
<input type="checkbox" checked id="chk2">
<label for="chk2">Automatic scrolling on update</label>
</div>
<script src="<?php echo fileversion('scripts/pi-hole/js/taillog.js'); ?>"></script>