Files
DnsServer/DnsServerCore/www/js/logs.js

487 lines
19 KiB
JavaScript

/*
Technitium DNS Server
Copyright (C) 2025 Shreyas Zare (shreyas@technitium.com)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
$(function () {
$("#optQueryLogsAppName").change(function () {
if (appsList == null)
return;
var appName = $("#optQueryLogsAppName").val();
var optClassPaths = "";
for (var i = 0; i < appsList.length; i++) {
if (appsList[i].name == appName) {
for (var j = 0; j < appsList[i].dnsApps.length; j++) {
if (appsList[i].dnsApps[j].isQueryLogs)
optClassPaths += "<option>" + appsList[i].dnsApps[j].classPath + "</option>";
}
break;
}
}
$("#optQueryLogsClassPath").html(optClassPaths);
$("#txtAddEditRecordDataData").val("");
});
$("#optQueryLogsEntriesPerPage").change(function () {
localStorage.setItem("optQueryLogsEntriesPerPage", $("#optQueryLogsEntriesPerPage").val());
});
var optQueryLogsEntriesPerPage = localStorage.getItem("optQueryLogsEntriesPerPage");
if (optQueryLogsEntriesPerPage != null)
$("#optQueryLogsEntriesPerPage").val(optQueryLogsEntriesPerPage);
});
function refreshLogsTab() {
if ($("#logsTabListLogViewer").hasClass("active"))
refreshLogFilesList();
else if ($("#logsTabListQueryLogs").hasClass("active"))
refreshQueryLogsTab();
}
function refreshLogFilesList() {
var lstLogFiles = $("#lstLogFiles");
HTTPRequest({
url: "/api/logs/list?token=" + sessionData.token,
success: function (responseJSON) {
var logFiles = responseJSON.response.logFiles;
var list = "<div class=\"log\" style=\"font-size: 14px; padding-bottom: 6px;\"><a href=\"#\" onclick=\"deleteAllStats(); return false;\"><b>[delete all stats]</b></a></div>";
if (logFiles.length == 0) {
list += "<div class=\"log\">No Log Was Found</div>";
}
else {
list += "<div class=\"log\" style=\"font-size: 14px; padding-bottom: 6px;\"><a href=\"#\" onclick=\"deleteAllLogs(); return false;\"><b>[delete all logs]</b></a></div>";
for (var i = 0; i < logFiles.length; i++) {
var logFile = logFiles[i];
list += "<div class=\"log\"><a href=\"#\" onclick=\"viewLog('" + logFile.fileName + "'); return false;\">" + logFile.fileName + " [" + logFile.size + "]</a></div>"
}
}
lstLogFiles.html(list);
},
invalidToken: function () {
showPageLogin();
},
objLoaderPlaceholder: lstLogFiles
});
}
function viewLog(logFile) {
var divLogViewer = $("#divLogViewer");
var txtLogViewerTitle = $("#txtLogViewerTitle");
var divLogViewerLoader = $("#divLogViewerLoader");
var preLogViewerBody = $("#preLogViewerBody");
txtLogViewerTitle.text(logFile);
preLogViewerBody.hide();
divLogViewerLoader.show();
divLogViewer.show();
HTTPRequest({
url: "/api/logs/download?token=" + sessionData.token + "&fileName=" + encodeURIComponent(logFile) + "&limit=2",
isTextResponse: true,
success: function (response) {
divLogViewerLoader.hide();
preLogViewerBody.text(response);
preLogViewerBody.show();
},
objLoaderPlaceholder: divLogViewerLoader
});
}
function downloadLog() {
var logFile = $("#txtLogViewerTitle").text();
window.open("/api/logs/download?token=" + sessionData.token + "&fileName=" + encodeURIComponent(logFile) + "&ts=" + (new Date().getTime()), "_blank");
}
function deleteLog() {
var logFile = $("#txtLogViewerTitle").text();
if (!confirm("Are you sure you want to permanently delete the log file '" + logFile + "'?"))
return;
var btn = $("#btnDeleteLog").button('loading');
HTTPRequest({
url: "/api/logs/delete?token=" + sessionData.token + "&log=" + logFile,
success: function (responseJSON) {
refreshLogFilesList();
$("#divLogViewer").hide();
btn.button('reset');
showAlert("success", "Log Deleted!", "Log file was deleted successfully.");
},
error: function () {
btn.button('reset');
},
invalidToken: function () {
btn.button('reset');
showPageLogin();
}
});
}
function deleteAllLogs() {
if (!confirm("Are you sure you want to permanently delete all log files?"))
return;
HTTPRequest({
url: "/api/logs/deleteAll?token=" + sessionData.token,
success: function (responseJSON) {
refreshLogFilesList();
$("#divLogViewer").hide();
showAlert("success", "Logs Deleted!", "All log files were deleted successfully.");
},
invalidToken: function () {
showPageLogin();
}
});
}
function deleteAllStats() {
if (!confirm("Are you sure you want to permanently delete all stats files?"))
return;
HTTPRequest({
url: "/api/dashboard/stats/deleteAll?token=" + sessionData.token,
success: function (responseJSON) {
showAlert("success", "Stats Deleted!", "All stats files were deleted successfully.");
},
invalidToken: function () {
showPageLogin();
}
});
}
var appsList;
function refreshQueryLogsTab(doQueryLogs) {
var frmQueryLogs = $("#frmQueryLogs");
var divQueryLogsLoader = $("#divQueryLogsLoader");
var optQueryLogsAppName = $("#optQueryLogsAppName");
var optQueryLogsClassPath = $("#optQueryLogsClassPath");
var currentAppName = optQueryLogsAppName.val();
var currentClassPath = optQueryLogsClassPath.val();
var loader;
if (appsList == null) {
frmQueryLogs.hide();
loader = divQueryLogsLoader;
}
else {
optQueryLogsAppName.prop('disabled', true);
optQueryLogsClassPath.prop('disabled', true);
}
HTTPRequest({
url: "/api/apps/list?token=" + sessionData.token,
success: function (responseJSON) {
var apps = responseJSON.response.apps;
var optApps = "";
var optClassPaths = "";
for (var i = 0; i < apps.length; i++) {
for (var j = 0; j < apps[i].dnsApps.length; j++) {
if (apps[i].dnsApps[j].isQueryLogs) {
optApps += "<option>" + apps[i].name + "</option>";
if (currentAppName == null)
currentAppName = apps[i].name;
break;
}
}
}
for (var i = 0; i < apps.length; i++) {
if (apps[i].name == currentAppName) {
for (var j = 0; j < apps[i].dnsApps.length; j++) {
if (apps[i].dnsApps[j].isQueryLogs)
optClassPaths += "<option>" + apps[i].dnsApps[j].classPath + "</option>";
}
break;
}
}
optQueryLogsAppName.html(optApps);
optQueryLogsClassPath.html(optClassPaths);
if (currentAppName != null)
optQueryLogsAppName.val(currentAppName);
if (currentClassPath != null)
optQueryLogsClassPath.val(currentClassPath);
if (appsList == null) {
frmQueryLogs.show();
loader.hide();
}
else {
optQueryLogsAppName.prop('disabled', false);
optQueryLogsClassPath.prop('disabled', false);
}
appsList = apps;
if (doQueryLogs)
queryLogs();
},
error: function () {
if (appsList == null) {
frmQueryLogs.show();
}
else {
optQueryLogsAppName.prop('disabled', false);
optQueryLogsClassPath.prop('disabled', false);
}
},
invalidToken: function () {
showPageLogin();
},
objLoaderPlaceholder: loader
});
}
function queryLogs(pageNumber) {
var btn = $("#btnQueryLogs");
var divQueryLogsLoader = $("#divQueryLogsLoader");
var divQueryLogsTable = $("#divQueryLogsTable");
var name = $("#optQueryLogsAppName").val();
if (name == null) {
showAlert("warning", "Missing!", "Please install the 'Query Logs (Sqlite)' DNS App or any other DNS app that supports query logging feature.");
$("#optQueryLogsAppName").focus();
return false;
}
var classPath = $("#optQueryLogsClassPath").val();
if (classPath == null) {
showAlert("warning", "Missing!", "Please select a Class Path to query logs.");
$("#optQueryLogsClassPath").focus();
return false;
}
if (pageNumber == null)
pageNumber = $("#txtQueryLogPageNumber").val();
var entriesPerPage = Number($("#optQueryLogsEntriesPerPage").val());
if (entriesPerPage < 1)
entriesPerPage = 10;
var descendingOrder = $("#optQueryLogsDescendingOrder").val();
var start = $("#txtQueryLogStart").val();
if (start != "")
start = moment(start).toISOString();
var end = $("#txtQueryLogEnd").val();
if (end != "")
end = moment(end).toISOString();
var clientIpAddress = $("#txtQueryLogClientIpAddress").val();
var protocol = $("#optQueryLogsProtocol").val();
var responseType = $("#optQueryLogsResponseType").val();
var rcode = $("#optQueryLogsResponseCode").val();
var qname = $("#txtQueryLogQName").val();
var qtype = $("#txtQueryLogQType").val();
var qclass = $("#optQueryLogQClass").val();
divQueryLogsTable.hide();
divQueryLogsLoader.show();
btn.button('loading');
HTTPRequest({
url: "/api/logs/query?token=" + sessionData.token + "&name=" + encodeURIComponent(name) + "&classPath=" + encodeURIComponent(classPath) + "&pageNumber=" + pageNumber + "&entriesPerPage=" + entriesPerPage + "&descendingOrder=" + descendingOrder +
"&start=" + encodeURIComponent(start) + "&end=" + encodeURIComponent(end) + "&clientIpAddress=" + encodeURIComponent(clientIpAddress) + "&protocol=" + protocol + "&responseType=" + responseType + "&rcode=" + rcode +
"&qname=" + encodeURIComponent(qname) + "&qtype=" + qtype + "&qclass=" + qclass,
success: function (responseJSON) {
var tableHtml = "";
for (var i = 0; i < responseJSON.response.entries.length; i++) {
tableHtml += "<tr><td>" + responseJSON.response.entries[i].rowNumber + "</td><td>" +
moment(responseJSON.response.entries[i].timestamp).local().format("YYYY-MM-DD HH:mm:ss") + "</td><td>" +
responseJSON.response.entries[i].clientIpAddress + "</td><td>" +
responseJSON.response.entries[i].protocol + "</td><td>" +
responseJSON.response.entries[i].responseType + (responseJSON.response.entries[i].responseRtt == null ? "" : "<div style=\"font-size: 12px;\">(" + responseJSON.response.entries[i].responseRtt.toFixed(2) + " ms)</div>") + "</td><td>" +
responseJSON.response.entries[i].rcode + "</td><td style=\"word-break: break-all;\">" +
htmlEncode(responseJSON.response.entries[i].qname == "" ? "." : responseJSON.response.entries[i].qname) + "</td><td>" +
(responseJSON.response.entries[i].qtype == null ? "" : responseJSON.response.entries[i].qtype) + "</td><td>" +
(responseJSON.response.entries[i].qclass == null ? "" : responseJSON.response.entries[i].qclass) + "</td><td style=\"word-break: break-all;\">" +
htmlEncode(responseJSON.response.entries[i].answer) +
"</td><td align=\"right\"><div class=\"dropdown\"><a href=\"#\" id=\"btnQueryLogsRowOption" + i + "\" class=\"dropdown-toggle\" data-toggle=\"dropdown\" aria-haspopup=\"true\" aria-expanded=\"true\"><span class=\"glyphicon glyphicon-option-vertical\" aria-hidden=\"true\"></span></a><ul class=\"dropdown-menu dropdown-menu-right\">";
tableHtml += "<li><a href=\"#\" data-id=\"" + i + "\" onclick=\"queryDnsServer('" + responseJSON.response.entries[i].qname + "', '" + responseJSON.response.entries[i].qtype + "'); return false;\">Query DNS Server</a></li>";
switch (responseJSON.response.entries[i].responseType.toLowerCase()) {
case "blocked":
case "upstreamblocked":
case "cacheblocked":
tableHtml += "<li><a href=\"#\" data-id=\"" + i + "\" data-domain=\"" + htmlEncode(responseJSON.response.entries[i].qname) + "\" onclick=\"allowDomain(this, 'btnQueryLogsRowOption'); return false;\">Allow Domain</a></li>";
break;
default:
tableHtml += "<li><a href=\"#\" data-id=\"" + i + "\" data-domain=\"" + htmlEncode(responseJSON.response.entries[i].qname) + "\" onclick=\"blockDomain(this, 'btnQueryLogsRowOption'); return false;\">Block Domain</a></li>";
break;
}
tableHtml += "</ul></div></td></tr>";
}
var paginationHtml = "";
if (responseJSON.response.pageNumber > 1) {
paginationHtml += "<li><a href=\"#\" aria-label=\"First\" onClick=\"queryLogs(1); return false;\"><span aria-hidden=\"true\">&laquo;</span></a></li>";
paginationHtml += "<li><a href=\"#\" aria-label=\"Previous\" onClick=\"queryLogs(" + (responseJSON.response.pageNumber - 1) + "); return false;\"><span aria-hidden=\"true\">&lsaquo;</span></a></li>";
}
var pageStart = responseJSON.response.pageNumber - 5;
if (pageStart < 1)
pageStart = 1;
var pageEnd = pageStart + 9;
if (pageEnd > responseJSON.response.totalPages) {
var endDiff = pageEnd - responseJSON.response.totalPages;
pageEnd = responseJSON.response.totalPages;
pageStart -= endDiff;
if (pageStart < 1)
pageStart = 1;
}
for (var i = pageStart; i <= pageEnd; i++) {
if (i == responseJSON.response.pageNumber)
paginationHtml += "<li class=\"active\"><a href=\"#\" onClick=\"queryLogs(" + i + "); return false;\">" + i + "</a></li>";
else
paginationHtml += "<li><a href=\"#\" onClick=\"queryLogs(" + i + "); return false;\">" + i + "</a></li>";
}
if (responseJSON.response.pageNumber < responseJSON.response.totalPages) {
paginationHtml += "<li><a href=\"#\" aria-label=\"Next\" onClick=\"queryLogs(" + (responseJSON.response.pageNumber + 1) + "); return false;\"><span aria-hidden=\"true\">&rsaquo;</span></a></li>";
paginationHtml += "<li><a href=\"#\" aria-label=\"Last\" onClick=\"queryLogs(-1); return false;\"><span aria-hidden=\"true\">&raquo;</span></a></li>";
}
$("#tableQueryLogsBody").html(tableHtml);
var statusHtml;
if (responseJSON.response.entries.length > 0)
statusHtml = responseJSON.response.entries[0].rowNumber + "-" + responseJSON.response.entries[responseJSON.response.entries.length - 1].rowNumber + " (" + responseJSON.response.entries.length + ") of " + responseJSON.response.totalEntries + " logs (page " + responseJSON.response.pageNumber + " of " + responseJSON.response.totalPages + ")";
else
statusHtml = "0 logs";
$("#tableQueryLogsTopStatus").html(statusHtml);
$("#tableQueryLogsTopPagination").html(paginationHtml);
$("#tableQueryLogsFooterStatus").html(statusHtml);
$("#tableQueryLogsFooterPagination").html(paginationHtml);
btn.button('reset');
divQueryLogsLoader.hide();
divQueryLogsTable.show();
},
error: function () {
btn.button('reset');
},
invalidToken: function () {
btn.button('reset');
showPageLogin();
},
objLoaderPlaceholder: divQueryLogsLoader
});
}
function showQueryLogs(domain, clientIp) {
$("#frmQueryLogs").trigger("reset");
if (domain != null)
$("#txtQueryLogQName").val(domain);
if (clientIp != null)
$("#txtQueryLogClientIpAddress").val(clientIp);
$("#mainPanelTabListDashboard").removeClass("active");
$("#mainPanelTabPaneDashboard").removeClass("active");
$("#mainPanelTabListLogs").addClass("active");
$("#mainPanelTabPaneLogs").addClass("active");
$("#logsTabListLogViewer").removeClass("active");
$("#logsTabPaneLogViewer").removeClass("active");
$("#logsTabListQueryLogs").addClass("active");
$("#logsTabPaneQueryLogs").addClass("active");
$("#modalTopStats").modal("hide");
refreshQueryLogsTab(true);
}
function exportQueryLogsCsv() {
var name = $("#optQueryLogsAppName").val();
if (name == null) {
showAlert("warning", "Missing!", "Please install the 'Query Logs (Sqlite)' DNS App or any other DNS app that supports query logging feature.");
$("#optQueryLogsAppName").focus();
return false;
}
var classPath = $("#optQueryLogsClassPath").val();
if (classPath == null) {
showAlert("warning", "Missing!", "Please select a Class Path to query logs.");
$("#optQueryLogsClassPath").focus();
return false;
}
var start = $("#txtQueryLogStart").val();
if (start != "")
start = moment(start).toISOString();
var end = $("#txtQueryLogEnd").val();
if (end != "")
end = moment(end).toISOString();
var clientIpAddress = $("#txtQueryLogClientIpAddress").val();
var protocol = $("#optQueryLogsProtocol").val();
var responseType = $("#optQueryLogsResponseType").val();
var rcode = $("#optQueryLogsResponseCode").val();
var qname = $("#txtQueryLogQName").val();
var qtype = $("#txtQueryLogQType").val();
var qclass = $("#optQueryLogQClass").val();
window.open("/api/logs/export?token=" + sessionData.token + "&name=" + encodeURIComponent(name) + "&classPath=" + encodeURIComponent(classPath) +
"&start=" + encodeURIComponent(start) + "&end=" + encodeURIComponent(end) + "&clientIpAddress=" + encodeURIComponent(clientIpAddress) +
"&protocol=" + protocol + "&responseType=" + responseType + "&rcode=" + rcode + "&qname=" + encodeURIComponent(qname) + "&qtype=" + qtype + "&qclass=" + qclass
, "_blank");
}